/*******************************************************************************
* Copyright (c) 2006, 2016 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.cdt.dsf.debug.ui.viewmodel.launch;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.RejectedExecutionException;
import org.eclipse.cdt.core.IAddress;
import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.datamodel.DataModelInitializedEvent;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
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.IContainerSuspendedDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent;
import org.eclipse.cdt.dsf.debug.service.IStack;
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMData;
import org.eclipse.cdt.dsf.debug.ui.IDsfDebugUIConstants;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController.SteppingTimedOutEvent;
import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.dsf.ui.concurrent.ViewerDataRequestMonitor;
import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMContext;
import org.eclipse.cdt.dsf.ui.viewmodel.IVMContext;
import org.eclipse.cdt.dsf.ui.viewmodel.ModelProxyInstalledEvent;
import org.eclipse.cdt.dsf.ui.viewmodel.VMChildrenUpdate;
import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta;
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode;
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider;
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.IElementPropertiesProvider;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.IPropertiesUpdate;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelAttribute;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelColumnInfo;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelImage;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelText;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.PropertiesBasedLabelProvider;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementCompareRequest;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoRequest;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.ui.IMemento;
public class StackFramesVMNode extends AbstractDMVMNode
implements IElementLabelProvider, IElementMementoProvider, IElementPropertiesProvider
{
/**
* @since 2.0
*/
public static final String PROP_IS_INCOMPLETE_STACK_MARKER = "is_incomplete_stack_marker"; //$NON-NLS-1$
/**
* View model context representing the end of an incomplete stack.
*
* @since 1.1
*/
public class IncompleteStackVMContext extends AbstractVMContext {
private final int fLevel;
private final IExecutionDMContext fDmc;
public IncompleteStackVMContext(IExecutionDMContext dmc, int level) {
super(StackFramesVMNode.this);
fDmc = dmc;
fLevel = level;
}
public int getLevel() {
return fLevel;
}
public IExecutionDMContext getExecutionDMContext() {
return fDmc;
}
@Override
public boolean equals(Object obj) {
return obj instanceof IncompleteStackVMContext &&
((IncompleteStackVMContext)obj).fDmc.equals(fDmc);
}
@Override
public int hashCode() {
return fDmc.hashCode();
}
}
/**
* Temporary stack frame limit to allow incremental stack updates.
*/
private Map<IExecutionDMContext, Integer> fTemporaryLimits = new HashMap<IExecutionDMContext, Integer>();
/**
* The label provider delegate. This VM node will delegate label updates to this provider
* which can be created by sub-classes.
*
* @since 2.0
*/
private IElementLabelProvider fLabelProvider;
public StackFramesVMNode(AbstractDMVMProvider provider, DsfSession session) {
super(provider, session, IStack.IFrameDMContext.class);
fLabelProvider = createLabelProvider();
}
@Override
public String toString() {
return "StackFramesVMNode(" + getSession().getId() + ")"; //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* Creates the label provider delegate. This VM node will delegate label
* updates to this provider which can be created by sub-classes.
*
* @return Returns the label provider for this node.
*
* @since 2.0
*/
protected IElementLabelProvider createLabelProvider() {
PropertiesBasedLabelProvider provider = new PropertiesBasedLabelProvider();
provider.setColumnInfo(
PropertiesBasedLabelProvider.ID_COLUMN_NO_COLUMNS,
new LabelColumnInfo(new LabelAttribute[] {
new LabelText(
MessagesForLaunchVM.StackFramesVMNode_No_columns__Incomplete_stack_marker__text_format,
new String[] { PROP_IS_INCOMPLETE_STACK_MARKER })
{
@Override
public boolean isEnabled(IStatus status, Map<String, Object> properties) {
return Boolean.TRUE.equals(properties.get(PROP_IS_INCOMPLETE_STACK_MARKER));
}
},
new LabelText(
MessagesForLaunchVM.StackFramesVMNode_No_columns__text_format,
new String[] {
ILaunchVMConstants.PROP_FRAME_ADDRESS,
ILaunchVMConstants.PROP_FRAME_FUNCTION,
ILaunchVMConstants.PROP_FRAME_FILE,
ILaunchVMConstants.PROP_FRAME_LINE,
ILaunchVMConstants.PROP_FRAME_COLUMN,
ILaunchVMConstants.PROP_FRAME_MODULE})
{
@Override
public boolean isEnabled(IStatus status, java.util.Map<String,Object> properties) {
Integer line = (Integer)properties.get(ILaunchVMConstants.PROP_FRAME_LINE);
String file = (String)properties.get(ILaunchVMConstants.PROP_FRAME_FILE);
String function = (String)properties.get(ILaunchVMConstants.PROP_FRAME_FUNCTION);
return line != null && line >= 0 && file != null && !file.isEmpty() &&
function != null && function.contains(")"); //$NON-NLS-1$
};
},
new LabelText(
MessagesForLaunchVM.StackFramesVMNode_No_columns__add_parens__text_format,
new String[] {
ILaunchVMConstants.PROP_FRAME_ADDRESS,
ILaunchVMConstants.PROP_FRAME_FUNCTION,
ILaunchVMConstants.PROP_FRAME_FILE,
ILaunchVMConstants.PROP_FRAME_LINE,
ILaunchVMConstants.PROP_FRAME_COLUMN,
ILaunchVMConstants.PROP_FRAME_MODULE})
{
@Override
public boolean isEnabled(IStatus status, java.util.Map<String,Object> properties) {
Integer line = (Integer)properties.get(ILaunchVMConstants.PROP_FRAME_LINE);
String file = (String)properties.get(ILaunchVMConstants.PROP_FRAME_FILE);
String function = (String)properties.get(ILaunchVMConstants.PROP_FRAME_FUNCTION);
return line != null && line >= 0 && file != null && !file.isEmpty() &&
(function == null || !function.contains(")")); //$NON-NLS-1$
};
},
new LabelText(
MessagesForLaunchVM.StackFramesVMNode_No_columns__No_line__text_format,
new String[] {
ILaunchVMConstants.PROP_FRAME_ADDRESS,
ILaunchVMConstants.PROP_FRAME_FUNCTION,
ILaunchVMConstants.PROP_FRAME_MODULE})
{
@Override
public boolean isEnabled(IStatus status, java.util.Map<String,Object> properties) {
String function = (String)properties.get(ILaunchVMConstants.PROP_FRAME_FUNCTION);
String module = (String)properties.get(ILaunchVMConstants.PROP_FRAME_MODULE);
return function != null && !function.isEmpty() && function.contains(")") && //$NON-NLS-1$
module != null && !module.isEmpty();
};
},
new LabelText(
MessagesForLaunchVM.StackFramesVMNode_No_columns__No_line__add_parens__text_format,
new String[] {
ILaunchVMConstants.PROP_FRAME_ADDRESS,
ILaunchVMConstants.PROP_FRAME_FUNCTION,
ILaunchVMConstants.PROP_FRAME_MODULE})
{
@Override
public boolean isEnabled(IStatus status, java.util.Map<String,Object> properties) {
String function = (String)properties.get(ILaunchVMConstants.PROP_FRAME_FUNCTION);
String module = (String)properties.get(ILaunchVMConstants.PROP_FRAME_MODULE);
return function != null && !function.isEmpty() && !function.contains(")") && //$NON-NLS-1$
module != null && !module.isEmpty();
};
},
new LabelText(
MessagesForLaunchVM.StackFramesVMNode_No_columns__No_function__text_format,
new String[] {
ILaunchVMConstants.PROP_FRAME_ADDRESS,
ILaunchVMConstants.PROP_FRAME_MODULE})
{
@Override
public boolean isEnabled(IStatus status, java.util.Map<String,Object> properties) {
String module = (String)properties.get(ILaunchVMConstants.PROP_FRAME_MODULE);
return module != null && !module.isEmpty();
};
},
new LabelText(
MessagesForLaunchVM.StackFramesVMNode_No_columns__No_module__text_format,
new String[] {
ILaunchVMConstants.PROP_FRAME_ADDRESS,
ILaunchVMConstants.PROP_FRAME_FUNCTION})
{
@Override
public boolean isEnabled(IStatus status, java.util.Map<String,Object> properties) {
String function = (String)properties.get(ILaunchVMConstants.PROP_FRAME_FUNCTION);
return function != null && !function.isEmpty() && function.contains(")"); //$NON-NLS-1$
};
},
new LabelText(
MessagesForLaunchVM.StackFramesVMNode_No_columns__No_module__add_parens__text_format,
new String[] {
ILaunchVMConstants.PROP_FRAME_ADDRESS,
ILaunchVMConstants.PROP_FRAME_FUNCTION})
{
@Override
public boolean isEnabled(IStatus status, java.util.Map<String,Object> properties) {
String function = (String)properties.get(ILaunchVMConstants.PROP_FRAME_FUNCTION);
return function != null && !function.isEmpty() && !function.contains(")"); //$NON-NLS-1$
};
},
new LabelText(
MessagesForLaunchVM.StackFramesVMNode_No_columns__Address_only__text_format,
new String[] { ILaunchVMConstants.PROP_FRAME_ADDRESS }),
new LabelImage(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_STACKFRAME_RUNNING)) {
{ setPropertyNames(new String[] { ILaunchVMConstants.PROP_IS_SUSPENDED }); }
@Override
public boolean isEnabled(IStatus status, java.util.Map<String,Object> properties) {
return Boolean.FALSE.equals( properties.get(ILaunchVMConstants.PROP_IS_SUSPENDED) );
};
},
new LabelImage(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_STACKFRAME))
}));
return provider;
}
/*
* (non-Javadoc)
* @see org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode#updateHasElementsInSessionThread(org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate)
*/
@Override
protected void updateHasElementsInSessionThread(IHasChildrenUpdate update) {
IRunControl runControl = getServicesTracker().getService(IRunControl.class);
IExecutionDMContext execCtx = findDmcInPath(update.getViewerInput(), update.getElementPath(), IExecutionDMContext.class);
if (runControl == null || execCtx == null) {
handleFailedUpdate(update);
return;
}
update.setHasChilren(runControl.isSuspended(execCtx) || runControl.isStepping(execCtx));
update.done();
}
@Override
protected void updateElementCountInSessionThread(final IChildrenCountUpdate update) {
IStack stackService = getServicesTracker().getService(IStack.class);
final IExecutionDMContext execDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IExecutionDMContext.class);
if (stackService == null || execDmc == null) {
handleFailedUpdate(update);
return;
}
final int stackFrameLimit= getStackFrameLimit(execDmc);
stackService.getStackDepth(
execDmc, stackFrameLimit == Integer.MAX_VALUE ? 0 : stackFrameLimit + 1,
new ViewerDataRequestMonitor<Integer>(getSession().getExecutor(), update) {
@Override
public void handleCompleted() {
if (!isSuccess()) {
handleFailedUpdate(update);
return;
}
int stackDepth= getData();
if (stackFrameLimit < stackDepth) {
stackDepth = stackFrameLimit + 1;
}
update.setChildCount(stackDepth);
update.done();
}
});
}
/*
* (non-Javadoc)
* @see org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode#updateElementsInSessionThread(org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate)
*/
@Override
protected void updateElementsInSessionThread(final IChildrenUpdate update) {
IStack stackService = getServicesTracker().getService(IStack.class);
final IExecutionDMContext execDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IExecutionDMContext.class);
if (stackService == null || execDmc == null) {
handleFailedUpdate(update);
return;
}
final int stackFrameLimit= getStackFrameLimit(execDmc);
final int startIndex= update.getOffset();
if (startIndex == 0 && update.getLength() == 1) {
// Requesting top stack frame only
stackService.getTopFrame(
execDmc,
new ViewerDataRequestMonitor<IFrameDMContext>(getSession().getExecutor(), update) {
@Override
public void handleCompleted() {
if (!isSuccess()) {
handleFailedUpdate(update);
return;
}
update.setChild(createVMContext(getData()), 0);
update.done();
}
});
} else {
if (startIndex >= 0 && update.getLength() > 0) {
// partial stack dump
int endIndex= startIndex + update.getLength() - 1;
if (startIndex < stackFrameLimit && endIndex >= stackFrameLimit) {
endIndex = stackFrameLimit - 1;
}
stackService.getFrames(
execDmc,
startIndex,
endIndex,
new ViewerDataRequestMonitor<IFrameDMContext[]>(getSession().getExecutor(), update) {
@Override
public void handleCompleted() {
if (!isSuccess()) {
handleFailedUpdate(update);
return;
}
IFrameDMContext[] frames = getData();
fillUpdateWithVMCs(update, frames, startIndex);
if (startIndex + update.getLength() > stackFrameLimit) {
update.setChild(new IncompleteStackVMContext(execDmc, stackFrameLimit), stackFrameLimit);
}
update.done();
}
});
} else {
// full stack dump
stackService.getFrames(
execDmc,
new ViewerDataRequestMonitor<IFrameDMContext[]>(getSession().getExecutor(), update) {
@Override
public void handleCompleted() {
if (!isSuccess()) {
handleFailedUpdate(update);
return;
}
IFrameDMContext[] frames = getData();
if (frames.length > stackFrameLimit) {
IFrameDMContext[] tmpFrames = new IFrameDMContext[stackFrameLimit];
System.arraycopy(frames, 0, tmpFrames, 0, stackFrameLimit);
frames = tmpFrames;
update.setChild(new IncompleteStackVMContext(execDmc, stackFrameLimit), stackFrameLimit);
}
fillUpdateWithVMCs(update, frames);
update.done();
}
});
}
}
}
@Override
public void update(final ILabelUpdate[] updates) {
fLabelProvider.update(updates);
}
/**
* @see IElementPropertiesProvider#update(IPropertiesUpdate[])
*
* @since 2.0
*/
@Override
public void update(final IPropertiesUpdate[] updates) {
try {
getSession().getExecutor().execute(new DsfRunnable() {
@Override
public void run() {
updatePropertiesInSessionThread(updates);
}});
} catch (RejectedExecutionException e) {
for (IPropertiesUpdate update : updates) {
handleFailedUpdate(update);
}
}
}
/**
* @since 2.0
*/
@ConfinedToDsfExecutor("getSession().getExecutor()")
protected void updatePropertiesInSessionThread(final IPropertiesUpdate[] updates) {
IStack service = getServicesTracker().getService(IStack.class);
for (final IPropertiesUpdate update : updates) {
if (service == null) {
handleFailedUpdate(update);
continue;
}
if (update.getElement() instanceof IncompleteStackVMContext) {
update.setProperty(PROP_IS_INCOMPLETE_STACK_MARKER, Boolean.TRUE);
update.done();
continue;
}
IFrameDMContext dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IFrameDMContext.class);
if (dmc == null) {
handleFailedUpdate(update);
continue;
}
IRunControl runControlService = getServicesTracker().getService(IRunControl.class);
IExecutionDMContext execDmc = DMContexts.getAncestorOfType(dmc, IExecutionDMContext.class);
if (execDmc != null && runControlService != null) {
update.setProperty(ILaunchVMConstants.PROP_IS_SUSPENDED, runControlService.isSuspended(execDmc));
update.setProperty(ILaunchVMConstants.PROP_IS_STEPPING, runControlService.isStepping(execDmc));
} else {
update.setStatus(DsfUIPlugin.newErrorStatus(IDsfStatusConstants.INVALID_HANDLE, "Invalid context or service not available", null)); //$NON-NLS-1$
}
service.getFrameData(
dmc,
new ViewerDataRequestMonitor<IFrameDMData>(getSession().getExecutor(), update) {
@Override
protected void handleSuccess() {
fillFrameDataProperties(update, getData());
update.done();
}
});
}
}
protected void fillFrameDataProperties(IPropertiesUpdate update, IFrameDMData data) {
IAddress address = data.getAddress();
if (address != null) {
update.setProperty(ILaunchVMConstants.PROP_FRAME_ADDRESS, "0x" + address.toString(16)); //$NON-NLS-1$
}
String fileName = data.getFile();
if (fileName != null) {
Object showFullPathPreference = getVMProvider().getPresentationContext().getProperty(IDsfDebugUIConstants.DEBUG_VIEW_SHOW_FULL_PATH_PROPERTY);
if (showFullPathPreference instanceof Boolean && (Boolean)showFullPathPreference == false) {
fileName = new Path(fileName).lastSegment();
}
}
update.setProperty(ILaunchVMConstants.PROP_FRAME_FILE, fileName);
update.setProperty(ILaunchVMConstants.PROP_FRAME_FUNCTION, data.getFunction());
update.setProperty(ILaunchVMConstants.PROP_FRAME_LINE, data.getLine());
update.setProperty(ILaunchVMConstants.PROP_FRAME_COLUMN, data.getColumn());
update.setProperty(ILaunchVMConstants.PROP_FRAME_MODULE, data.getModule());
}
protected void fillColumnLabel(IFrameDMContext dmContext, IFrameDMData dmData, String columnId, int idx, ILabelUpdate update)
{
if (idx != 0) return;
final IExecutionDMContext execDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IExecutionDMContext.class);
if (execDmc == null) {
return;
}
IRunControl runControlService = getServicesTracker().getService(IRunControl.class);
SteppingController stepQueueMgr = execDmc.getAdapter(SteppingController.class);
if (runControlService == null || stepQueueMgr == null) return;
String imageKey = null;
if (runControlService.isSuspended(execDmc) ||
(runControlService.isStepping(execDmc) && !stepQueueMgr.isSteppingTimedOut(execDmc)))
{
imageKey = IDebugUIConstants.IMG_OBJS_STACKFRAME;
} else {
imageKey = IDebugUIConstants.IMG_OBJS_STACKFRAME_RUNNING;
}
update.setImageDescriptor(DebugUITools.getImageDescriptor(imageKey), 0);
//
// Finally, if all goes well, set the label.
//
StringBuilder label = new StringBuilder();
// Add the function name
if (dmData.getFunction() != null && dmData.getFunction().length() != 0) {
label.append(" "); //$NON-NLS-1$
label.append(dmData.getFunction());
label.append("()"); //$NON-NLS-1$
}
boolean hasFileName = dmData.getFile() != null && dmData.getFile().length() != 0;
// Add full file name
if (hasFileName) {
label.append(" at "); //$NON-NLS-1$
label.append(dmData.getFile());
}
// Add line number
if (dmData.getLine() >= 0) {
label.append(":"); //$NON-NLS-1$
label.append(dmData.getLine());
label.append(" "); //$NON-NLS-1$
}
// Add module
if (!hasFileName && (dmData.getModule() != null && dmData.getModule().length() != 0)) {
label.append(" "); //$NON-NLS-1$
label.append(dmData.getModule());
label.append(" "); //$NON-NLS-1$
}
// Add the address
if (dmData.getAddress() != null) {
label.append("- 0x").append(dmData.getAddress().toString(16)); //$NON-NLS-1$
}
// Set the label to the result listener
update.setLabel(label.toString(), 0);
}
/*
* (non-Javadoc)
* @see org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode#getContextsForEvent(org.eclipse.cdt.dsf.ui.viewmodel.VMDelta, java.lang.Object, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
*/
@Override
public void getContextsForEvent(final VMDelta parentDelta, Object e, final DataRequestMonitor<IVMContext[]> rm) {
if (e instanceof ModelProxyInstalledEvent || e instanceof DataModelInitializedEvent) {
// Retrieve the list of stack frames, and mark the top frame to be selected.
getVMProvider().updateNode(
this,
new VMChildrenUpdate(
parentDelta, getVMProvider().getPresentationContext(), 0, 1,
new DataRequestMonitor<List<Object>>(getExecutor(), rm) {
@Override
public void handleCompleted() {
if (isSuccess() && getData().size() != 0) {
rm.setData(new IVMContext[] { (IVMContext)getData().get(0) });
} else {
// In case of errors, return an empty set of frames.
rm.setData(new IVMContext[0]);
}
rm.done();
}
})
);
return;
}
super.getContextsForEvent(parentDelta, e, rm);
}
/*
* (non-Javadoc)
* @see org.eclipse.cdt.dsf.ui.viewmodel.IVMNode#getDeltaFlags(java.lang.Object)
*/
@Override
public int getDeltaFlags(Object e) {
// This node generates delta if the timers have changed, or if the
// label has changed.
if (e instanceof ISuspendedDMEvent) {
return IModelDelta.CONTENT | IModelDelta.EXPAND | IModelDelta.SELECT;
} else if (e instanceof FullStackRefreshEvent &&
!(((FullStackRefreshEvent)e).getTriggeringEvent() instanceof IContainerSuspendedDMEvent) )
{
return IModelDelta.CONTENT;
} else if (e instanceof SteppingTimedOutEvent) {
return IModelDelta.CONTENT;
} else if (e instanceof ModelProxyInstalledEvent || e instanceof DataModelInitializedEvent) {
return IModelDelta.SELECT | IModelDelta.EXPAND;
} else if (e instanceof ExpandStackEvent) {
return IModelDelta.CONTENT;
} else if (e instanceof IExitedDMEvent) {
// Do not generate a delta for this event, but do clear the
// internal stack frame limit to avoid a memory leak.
resetStackFrameLimit( ((IExitedDMEvent)e).getDMContext() );
return IModelDelta.NO_CHANGE;
} else if (e instanceof PropertyChangeEvent) {
String property = ((PropertyChangeEvent)e).getProperty();
if (IDsfDebugUIConstants.PREF_STACK_FRAME_LIMIT_ENABLE.equals(property)
|| IDsfDebugUIConstants.PREF_STACK_FRAME_LIMIT.equals(property))
{
return IModelDelta.CONTENT;
} else if (IDsfDebugUIConstants.DEBUG_VIEW_SHOW_FULL_PATH_PROPERTY.equals(property)) {
return IModelDelta.STATE;
}
} else {
}
return IModelDelta.NO_CHANGE;
}
/*
* (non-Javadoc)
* @see org.eclipse.cdt.dsf.ui.viewmodel.IVMNode#buildDelta(java.lang.Object, org.eclipse.cdt.dsf.ui.viewmodel.VMDelta, int, org.eclipse.cdt.dsf.concurrent.RequestMonitor)
*/
@Override
public void buildDelta(final Object e, final VMDelta parent, final int nodeOffset, final RequestMonitor rm) {
if (e instanceof IContainerSuspendedDMEvent) {
// Clear the limit on the stack frames for all stack frames under a given container.
resetStackFrameLimit( ((IContainerSuspendedDMEvent)e).getDMContext() );
IContainerSuspendedDMEvent csEvent = (IContainerSuspendedDMEvent)e;
IExecutionDMContext triggeringCtx = csEvent.getTriggeringContexts().length != 0
? csEvent.getTriggeringContexts()[0] : null;
if (parent.getElement() instanceof IDMVMContext) {
IExecutionDMContext threadDmc = null;
threadDmc = DMContexts.getAncestorOfType( ((IDMVMContext)parent.getElement()).getDMContext(), IExecutionDMContext.class);
buildDeltaForSuspendedEvent(threadDmc, triggeringCtx, parent, nodeOffset, rm);
} else {
rm.done();
}
} else if (e instanceof FullStackRefreshEvent) {
FullStackRefreshEvent refreshEvent = (FullStackRefreshEvent)e;
if ( !(refreshEvent.getTriggeringEvent() instanceof IContainerSuspendedDMEvent)) {
// Don't refresh each stack frame when we get an event for the entire container
// Bug 386175
IExecutionDMContext execDmc = ((FullStackRefreshEvent)e).getDMContext();
buildDeltaForFullStackRefreshEvent(execDmc, execDmc, parent, nodeOffset, rm);
} else {
rm.done();
}
} else if (e instanceof ISuspendedDMEvent) {
resetStackFrameLimit( ((ISuspendedDMEvent)e).getDMContext() );
IExecutionDMContext execDmc = ((ISuspendedDMEvent)e).getDMContext();
buildDeltaForSuspendedEvent(execDmc, execDmc, parent, nodeOffset, rm);
} else if (e instanceof SteppingTimedOutEvent) {
buildDeltaForSteppingTimedOutEvent((SteppingTimedOutEvent)e, parent, nodeOffset, rm);
} else if (e instanceof ModelProxyInstalledEvent || e instanceof DataModelInitializedEvent) {
buildDeltaForModelProxyInstalledEvent(parent, nodeOffset, rm);
} else if (e instanceof ExpandStackEvent) {
IExecutionDMContext execDmc = ((ExpandStackEvent)e).getDMContext();
buildDeltaForExpandStackEvent(execDmc, parent, rm);
} else if (e instanceof PropertyChangeEvent) {
String property = ((PropertyChangeEvent)e).getProperty();
if (IDsfDebugUIConstants.PREF_STACK_FRAME_LIMIT_ENABLE.equals(property)
|| IDsfDebugUIConstants.PREF_STACK_FRAME_LIMIT.equals(property))
{
buildDeltaForStackFrameLimitPreferenceChangedEvent(parent, rm);
} else if (IDsfDebugUIConstants.DEBUG_VIEW_SHOW_FULL_PATH_PROPERTY.equals(property)) {
buildDeltaForShowFullPathPreferenceChangedEvent(parent, rm);
} else {
rm.done();
}
} else {
rm.done();
}
}
/**
* Builds the delta in response to a suspended event.
* <p>
* Default behavior is to expand the thread, repaint <strong>only</strong>
* the top stack frame and select it. The rest of the frames will be
* repainted after a short delay.
*
* @since 2.1
*/
protected void buildDeltaForSuspendedEvent(final IExecutionDMContext executionCtx, final IExecutionDMContext triggeringCtx, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor rm) {
// Check if we are building a delta for the thread that triggered the event.
// Only then expand the stack frames and select the top one.
if (executionCtx.equals(triggeringCtx)) {
// Always expand the thread node to show the stack frames.
parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.EXPAND);
// Retrieve the list of stack frames, and mark the top frame to be selected.
getVMProvider().updateNode(
this,
new VMChildrenUpdate(
parentDelta, getVMProvider().getPresentationContext(), 0, 1,
new DataRequestMonitor<List<Object>>(getExecutor(), rm) {
@Override
public void handleCompleted() {
final List<Object> data= getData();
if (data != null && data.size() != 0) {
parentDelta.addNode(data.get(0), 0, IModelDelta.SELECT | IModelDelta.STATE);
}
// Even in case of errors, complete the request monitor.
rm.done();
}
})
);
} else {
rm.done();
}
}
/**
* Builds a delta in response to automatic refresh event generated after
* every suspend event.
* <p>
* The default behavior is to check if the thread is
* still stepping or suspended and refresh the stack trace.
*
* @since 2.1
*/
protected void buildDeltaForFullStackRefreshEvent(final IExecutionDMContext executionCtx, final IExecutionDMContext triggeringCtx, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor rm) {
try {
getSession().getExecutor().execute(new DsfRunnable() {
@Override
public void run() {
IRunControl runControlService = getServicesTracker().getService(IRunControl.class);
IStack stackService = getServicesTracker().getService(IStack.class);
if (stackService == null || runControlService == null) {
// Required services have not initialized yet. Ignore the event.
rm.done();
return;
}
// Refresh the whole list of stack frames unless the target is already stepping the next command. In
// which case, the refresh will occur when the stepping sequence slows down or stops. Trying to
// refresh the whole stack trace with every step would slow down stepping too much.
if (triggeringCtx == null || !runControlService.isStepping(triggeringCtx)) {
parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT);
}
rm.done();
}
});
} catch (RejectedExecutionException e) {
// Session shut down, no delta to build.
rm.done();
}
}
/**
* Builds the delta in response to a time-out after stepping over a long
* executing function.
* <p>
* The default behavior is to repainting the stack frame images to
* show the running symbol.
*
* @since 2.1
*/
protected void buildDeltaForSteppingTimedOutEvent(final SteppingTimedOutEvent e, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor rm) {
// Repaint the stack frame images to have the running symbol.
//parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT);
rm.done();
}
/**
* Builds the delta in response the debug view being opened.
* <p>
* The default behavior is to retrieve the list of stack frames,
* and mark the top frame to be selected.
*
* @since 2.1
*/
protected void buildDeltaForModelProxyInstalledEvent(final VMDelta parentDelta, final int nodeOffset, final RequestMonitor rm) {
getVMProvider().updateNode(
this,
new VMChildrenUpdate(
parentDelta, getVMProvider().getPresentationContext(), -1, -1,
new DataRequestMonitor<List<Object>>(getExecutor(), rm) {
@Override
public void handleCompleted() {
if (isSuccess() && getData().size() != 0) {
parentDelta.addNode( getData().get(0), 0, IModelDelta.SELECT | IModelDelta.EXPAND);
}
rm.done();
}
})
);
}
/**
* Builds the delta in response to the user requesting to retrieve
* additional stack frames (changing the current stack frames limit).
* <p>
* The default behavior is to refresh the stack frames, and to select the
* first frame of the new frames that are retrieved.
*
* @since 2.1
*/
protected void buildDeltaForExpandStackEvent(IExecutionDMContext execDmc, final VMDelta parentDelta, final RequestMonitor rm) {
parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT);
// Retrieve the list of new stack frames, and mark the first frame to be selected.
final int offset = getStackFrameLimit(execDmc) / 2;
getVMProvider().updateNode(
this,
new VMChildrenUpdate(
parentDelta, getVMProvider().getPresentationContext(), offset, offset,
new DataRequestMonitor<List<Object>>(getExecutor(), rm) {
@Override
public void handleCompleted() {
final List<Object> data= getData();
if (data != null && data.size() != 0) {
parentDelta.addNode(data.get(0), offset, IModelDelta.SELECT);
}
rm.done();
}
})
);
}
/**
* Builds the delta in response to the stack frame limit preference changing.
* <p>
* Default behavior is to refresh the stack frames.
*
* @since 2.1
*/
protected void buildDeltaForStackFrameLimitPreferenceChangedEvent(final VMDelta parentDelta, final RequestMonitor rm) {
parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT);
rm.done();
}
private void buildDeltaForShowFullPathPreferenceChangedEvent(final VMDelta parentDelta, final RequestMonitor rm) {
parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.STATE);
rm.done();
}
private String produceFrameElementName( String viewName , IFrameDMContext frame ) {
/*
* We are addressing Bugzilla 211490 which wants the Register View to keep the same expanded
* state for registers for stack frames within the same thread. Different threads could have
* different register sets ( e.g. one thread may have floating point & another may not ). But
* within a thread we are enforcing the assumption that the register sets will be the same.
* So we make a more convenient work flow by keeping the same expansion when selecting amount
* stack frames within the same thread. We accomplish this by only differentiating by adding
* the level for the Expression/Variables view. Otherwise we do not delineate based on which
* view and this captures the Register View in its filter.
*/
if ( viewName.startsWith(IDebugUIConstants.ID_VARIABLE_VIEW) ||
viewName.startsWith(IDebugUIConstants.ID_EXPRESSION_VIEW) )
{
return "Frame." + frame.getLevel() + "." + frame.getSessionId(); //$NON-NLS-1$ //$NON-NLS-2$
}
else {
return "Frame" + frame.getSessionId(); //$NON-NLS-1$
}
}
/*
* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoProvider#compareElements(org.eclipse.debug.internal.ui.viewers.model.provisional.IElementCompareRequest[])
*/
@Override
public void compareElements(IElementCompareRequest[] requests) {
for ( IElementCompareRequest request : requests ) {
Object element = request.getElement();
IMemento memento = request.getMemento();
String mementoName = memento.getString("STACK_FRAME_MEMENTO_NAME"); //$NON-NLS-1$
if (mementoName != null) {
if (element instanceof IDMVMContext) {
IDMContext dmc = ((IDMVMContext)element).getDMContext();
if ( dmc instanceof IFrameDMContext) {
String elementName = produceFrameElementName( request.getPresentationContext().getId(), (IFrameDMContext) dmc );
request.setEqual( elementName.equals( mementoName ) );
}
}
}
request.done();
}
}
/*
* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoProvider#encodeElements(org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoRequest[])
*/
@Override
public void encodeElements(IElementMementoRequest[] requests) {
for ( IElementMementoRequest request : requests ) {
Object element = request.getElement();
IMemento memento = request.getMemento();
if (element instanceof IDMVMContext) {
IDMContext dmc = ((IDMVMContext)element).getDMContext();
if ( dmc instanceof IFrameDMContext) {
String elementName = produceFrameElementName( request.getPresentationContext().getId(), (IFrameDMContext) dmc );
memento.putString("STACK_FRAME_MEMENTO_NAME", elementName); //$NON-NLS-1$
}
}
request.done();
}
}
/**
* Get the current active stack frame limit. If no limit is applicable {@link Integer.MAX_VALUE} is returned.
*
* @return the current stack frame limit
*
* @since 1.1
*/
public int getStackFrameLimit(IExecutionDMContext execCtx) {
if (fTemporaryLimits.containsKey(execCtx)) {
return fTemporaryLimits.get(execCtx);
}
Object stackDepthLimit= getVMProvider().getPresentationContext().getProperty(IDsfDebugUIConstants.PREF_STACK_FRAME_LIMIT);
if (stackDepthLimit instanceof Integer) {
return (Integer)stackDepthLimit;
}
return Integer.MAX_VALUE;
}
/**
* Resets the temporary stack frame limit for the given execution context.
* The stack frame limit should be reset when the a thread is suspended.
*
* @param execCtx
*
* @since 2.1
*/
protected void resetStackFrameLimit(IExecutionDMContext execCtx) {
if (execCtx instanceof IContainerDMContext) {
for (Iterator<IExecutionDMContext> itr = fTemporaryLimits.keySet().iterator(); itr.hasNext();) {
IExecutionDMContext limitCtx = itr.next();
if (limitCtx.equals(execCtx) || DMContexts.isAncestorOf(limitCtx, execCtx)) {
itr.remove();
}
}
} else {
fTemporaryLimits.remove(execCtx);
}
}
/**
* Increment the stack frame limit by the default increment.
* This implementation doubles the current limit.
*
* @since 1.1
*/
public void incrementStackFrameLimit(IExecutionDMContext execCtx) {
final int stackFrameLimit= getStackFrameLimit(execCtx);
if (stackFrameLimit < Integer.MAX_VALUE / 2) {
fTemporaryLimits.put(execCtx, stackFrameLimit * 2);
} else {
fTemporaryLimits.put(execCtx, Integer.MAX_VALUE);
}
}
}