/******************************************************************************* * Copyright (c) 2008, 2015 Wind River Systems, Inc. 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 * Patrick Chuong (Texas Instruments) - Add support for icon overlay in the debug view (Bug 334566) * Dobrin Alexiev (Texas Instruments) - user groups support (bug 240208) *******************************************************************************/ package org.eclipse.cdt.dsf.debug.ui.viewmodel.launch; import java.util.List; import java.util.concurrent.RejectedExecutionException; 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.datamodel.IDMEvent; 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.IContainerResumedDMEvent; 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.IExecutionDMData; import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMData2; import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent; import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent; import org.eclipse.cdt.dsf.debug.service.IRunControl.IStartedDMEvent; import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason; 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.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.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.Status; import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; 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; /** * Abstract implementation of a container view model node. * Clients need to implement {@link #updateLabelInSessionThread(ILabelUpdate[])}. * * @since 1.1 */ public abstract class AbstractContainerVMNode extends AbstractExecutionContextVMNode implements IElementLabelProvider, IElementPropertiesProvider { /** * 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 AbstractContainerVMNode(AbstractDMVMProvider provider, DsfSession session) { super(provider, session, IRunControl.IContainerDMContext.class); fLabelProvider = createLabelProvider(); } /** * 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 ExecutionContextLabelText( MessagesForLaunchVM.AbstractContainerVMNode_No_columns__text_format, new String[] { ExecutionContextLabelText.PROP_NAME_KNOWN, PROP_NAME, ExecutionContextLabelText.PROP_ID_KNOWN, ILaunchVMConstants.PROP_ID }), new LabelText(MessagesForLaunchVM.AbstractContainerVMNode_No_columns__Error__label, new String[0]), new LabelImage(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_DEBUG_TARGET_SUSPENDED)) { { setPropertyNames(new String[] { ILaunchVMConstants.PROP_IS_SUSPENDED }); } @Override public boolean isEnabled(IStatus status, java.util.Map<String,Object> properties) { return Boolean.TRUE.equals(properties.get(ILaunchVMConstants.PROP_IS_SUSPENDED)); }; }, new LabelImage(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_DEBUG_TARGET)), })); return provider; } @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) { IRunControl service = getServicesTracker().getService(IRunControl.class); for (final IPropertiesUpdate update : updates) { if (service == null) { handleFailedUpdate(update); continue; } IExecutionDMContext dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IExecutionDMContext.class); if (dmc == null) { handleFailedUpdate(update); continue; } update.setProperty(ILaunchVMConstants.PROP_IS_SUSPENDED, service.isSuspended(dmc)); update.setProperty(ILaunchVMConstants.PROP_IS_STEPPING, service.isStepping(dmc)); service.getExecutionData( dmc, new ViewerDataRequestMonitor<IExecutionDMData>(getSession().getExecutor(), update) { @Override protected void handleSuccess() { fillExecutionDataProperties(update, getData()); update.done(); } }); } } protected void fillExecutionDataProperties(IPropertiesUpdate update, IExecutionDMData data) { StateChangeReason reason = data.getStateChangeReason(); if (reason != null) { update.setProperty(ILaunchVMConstants.PROP_STATE_CHANGE_REASON, data.getStateChangeReason().name()); } if (data instanceof IExecutionDMData2) { String details = ((IExecutionDMData2)data).getDetails(); if (details != null) { update.setProperty(ILaunchVMConstants.PROP_STATE_CHANGE_DETAILS, details); } } } @Override public void getContextsForEvent(VMDelta parentDelta, Object e, final DataRequestMonitor<IVMContext[]> rm) { if (e instanceof ModelProxyInstalledEvent || e instanceof DataModelInitializedEvent) { getContainerVMCForModelProxyInstallEvent( parentDelta, new DataRequestMonitor<VMContextInfo>(getExecutor(), rm) { @Override protected void handleCompleted() { if (isSuccess()) { rm.setData(new IVMContext[] { getData().fVMContext }); } else { rm.setData(new IVMContext[0]); } rm.done(); } }); } else { super.getContextsForEvent(parentDelta, e, rm); } } private static class VMContextInfo { final IVMContext fVMContext; final int fIndex; final boolean fIsSuspended; VMContextInfo(IVMContext vmContext, int index, boolean isSuspended) { fVMContext = vmContext; fIndex = index; fIsSuspended = isSuspended; } } private void getContainerVMCForModelProxyInstallEvent(VMDelta parentDelta, final DataRequestMonitor<VMContextInfo> rm) { getVMProvider().updateNode(this, new VMChildrenUpdate( parentDelta, getVMProvider().getPresentationContext(), -1, -1, new DataRequestMonitor<List<Object>>(getExecutor(), rm) { @Override protected void handleSuccess() { try { getSession().getExecutor().execute(new DsfRunnable() { @Override public void run() { final IRunControl runControl = getServicesTracker().getService(IRunControl.class); if (runControl != null) { int vmcIdx = -1; int suspendedVmcIdx = -1; for (int i = 0; i < getData().size(); i++) { if (getData().get(i) instanceof IDMVMContext) { IDMVMContext vmc = (IDMVMContext)getData().get(i); IContainerDMContext containerDmc = DMContexts.getAncestorOfType( vmc.getDMContext(), IContainerDMContext.class); if (containerDmc != null) { vmcIdx = vmcIdx < 0 ? i : vmcIdx; if (runControl.isSuspended(containerDmc)) { suspendedVmcIdx = suspendedVmcIdx < 0 ? i : suspendedVmcIdx; } } } } if (suspendedVmcIdx >= 0) { rm.setData(new VMContextInfo( (IVMContext)getData().get(suspendedVmcIdx), suspendedVmcIdx, true)); } else if (vmcIdx >= 0) { rm.setData(new VMContextInfo((IVMContext)getData().get(vmcIdx), vmcIdx, false)); } else { rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "No container available", null)); //$NON-NLS-1$ } rm.done(); } else { rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "No container available", null)); //$NON-NLS-1$ rm.done(); } } }); } catch (RejectedExecutionException e) { rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "", null)); //$NON-NLS-1$ rm.done(); } } })); } @Override public int getDeltaFlags(Object e) { IDMContext dmc = e instanceof IDMEvent<?> ? ((IDMEvent<?>)e).getDMContext() : null; if (e instanceof IContainerResumedDMEvent) { if (((IContainerResumedDMEvent)e).getReason() != IRunControl.StateChangeReason.STEP) { return IModelDelta.CONTENT; } } else if (e instanceof IContainerSuspendedDMEvent) { return IModelDelta.NO_CHANGE; } else if (e instanceof SteppingTimedOutEvent) { if (dmc instanceof IContainerDMContext) { return IModelDelta.CONTENT; } } else if (e instanceof IExitedDMEvent) { return IModelDelta.CONTENT; } else if (e instanceof IStartedDMEvent) { if (dmc instanceof IContainerDMContext) { return IModelDelta.EXPAND | IModelDelta.SELECT; } else { return IModelDelta.CONTENT; } } else if (e instanceof ModelProxyInstalledEvent || e instanceof DataModelInitializedEvent) { return IModelDelta.SELECT | IModelDelta.EXPAND; } else if (e instanceof StateChangedEvent) { return IModelDelta.STATE; } else if (e instanceof FullStackRefreshEvent && (((FullStackRefreshEvent)e).getTriggeringEvent() instanceof IContainerSuspendedDMEvent)) { return IModelDelta.CONTENT; } else if (e instanceof IResumedDMEvent) { return IModelDelta.STATE; } return IModelDelta.NO_CHANGE; } @Override public void buildDelta(Object e, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor requestMonitor) { IDMContext dmc = e instanceof IDMEvent<?> ? ((IDMEvent<?>)e).getDMContext() : null; if(e instanceof IContainerResumedDMEvent) { // Container resumed: // - If not stepping, update the container and the execution // contexts under it. // - If stepping, do nothing to avoid too many updates. If a // time-out is reached before the step completes, the // ISteppingTimedOutEvent will trigger a full refresh. if (((IContainerResumedDMEvent)e).getReason() != IRunControl.StateChangeReason.STEP) { parentDelta.addNode(createVMContext(((IDMEvent<?>)e).getDMContext()), IModelDelta.CONTENT); } } else if (e instanceof IContainerSuspendedDMEvent) { // Container suspended. Do nothing here to give the stack the // priority in updating. The container and threads will update as // a result of FullStackRefreshEvent. } else if (e instanceof SteppingTimedOutEvent) { // Stepping time-out indicates that a step operation is taking // a long time, and the view needs to be refreshed to show // the user that the program is running. // If the step was issued for the whole container refresh // the whole container. if (dmc instanceof IContainerDMContext) { parentDelta.addNode(createVMContext(dmc), IModelDelta.CONTENT); } } else if (e instanceof IExitedDMEvent) { // An exited event could either be for a thread within a container // or for the container itself. // If a container exited, refresh the parent element so that the // container may be removed. // If a thread exited within a container, refresh that container. if (dmc instanceof IContainerDMContext) { parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); } else { IContainerDMContext containerCtx = DMContexts.getAncestorOfType(dmc, IContainerDMContext.class); if (containerCtx != null) { parentDelta.addNode(createVMContext(containerCtx), IModelDelta.CONTENT); } } } else if (e instanceof IStartedDMEvent) { // A started event could either be for a thread within a container // or for the container itself. // If a container started, issue an expand and select event to // show the threads in the new container. // Note: the EXPAND flag implies refreshing the parent element. if (dmc instanceof IContainerDMContext) { parentDelta.addNode(createVMContext(dmc), IModelDelta.EXPAND | IModelDelta.SELECT); } else { IContainerDMContext containerCtx = DMContexts.getAncestorOfType(dmc, IContainerDMContext.class); if (containerCtx != null) { parentDelta.addNode(createVMContext(containerCtx), IModelDelta.CONTENT); } } } else if (e instanceof ModelProxyInstalledEvent || e instanceof DataModelInitializedEvent) { // Model Proxy install event is generated when the model is first // populated into the view. This happens when a new debug session // is started or when the view is first opened. // In both cases, if there are already thread containers in the debug model, // the desired user behavior is to show the containers and to select // the first thread. // If the container is suspended, do not select it, instead, // one of its threads will be selected. getContainerVMCForModelProxyInstallEvent( parentDelta, new DataRequestMonitor<VMContextInfo>(getExecutor(), requestMonitor) { @Override protected void handleCompleted() { if (isSuccess()) { parentDelta.addNode( getData().fVMContext, nodeOffset + getData().fIndex, IModelDelta.EXPAND | (getData().fIsSuspended ? 0 : IModelDelta.SELECT)); } requestMonitor.done(); } }); return; } else if (e instanceof StateChangedEvent) { // If there is a state change needed on the container, update the container if (dmc instanceof IContainerDMContext) parentDelta.addNode(createVMContext(dmc), IModelDelta.STATE); } else if (e instanceof FullStackRefreshEvent) { FullStackRefreshEvent refreshEvent = (FullStackRefreshEvent)e; if (refreshEvent.getTriggeringEvent() instanceof IContainerSuspendedDMEvent) { // For a full container suspended event, issue a single change when we get // a FullStackRefreshEvent. This avoids refreshing all threads, even those // that are not visible // bug 386175 IContainerSuspendedDMEvent containerTriggerEvent = (IContainerSuspendedDMEvent)refreshEvent.getTriggeringEvent(); buildDeltaForFullStackRefreshEvent((IContainerDMContext)refreshEvent.getDMContext(), containerTriggerEvent.getTriggeringContexts(), parentDelta, nodeOffset, requestMonitor); return; } } else if (e instanceof IResumedDMEvent) { // update the container node label IContainerDMContext containerCtx = DMContexts.getAncestorOfType(dmc, IContainerDMContext.class); parentDelta.addNode(createVMContext(containerCtx), IModelDelta.STATE); } requestMonitor.done(); } /** * Builds a delta in response to automatic refresh event generated after * every suspend event. * <p> * As opposed to the StackFrameVMNode handling of the refresh event, the * container handles only the refresh events for container suspended events, * and it refreshes the entire container. * <p> * The default behavior is to check if the thread is still stepping or * suspended and refresh the stack trace. */ protected void buildDeltaForFullStackRefreshEvent(final IContainerDMContext containerCtx, final IExecutionDMContext[] triggeringCtxs, 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); if (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. boolean isStepping = false; for (IExecutionDMContext triggeringCtx : triggeringCtxs) { if (runControlService.isStepping(triggeringCtx)) { isStepping = true; break; } } if (!isStepping) { parentDelta.addNode(createVMContext(containerCtx), IModelDelta.CONTENT); } rm.done(); } }); } catch (RejectedExecutionException e) { // Session shut down, no delta to build. rm.done(); } } }