/******************************************************************************* * Copyright (c) 2007, 2011 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 *******************************************************************************/ package org.eclipse.tm.internal.tcf.debug.ui.model; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.IExpressionManager; import org.eclipse.debug.core.IExpressionsListener; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.commands.IDisconnectHandler; import org.eclipse.debug.core.commands.IDropToFrameHandler; import org.eclipse.debug.core.commands.IResumeHandler; import org.eclipse.debug.core.commands.IStepIntoHandler; import org.eclipse.debug.core.commands.IStepOverHandler; import org.eclipse.debug.core.commands.IStepReturnHandler; import org.eclipse.debug.core.commands.ISuspendHandler; import org.eclipse.debug.core.commands.ITerminateHandler; import org.eclipse.debug.core.model.IDebugModelProvider; import org.eclipse.debug.core.model.IExpression; import org.eclipse.debug.core.model.IMemoryBlockRetrieval; import org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension; import org.eclipse.debug.core.model.ISourceLocator; import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer; 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.IColumnPresentation; import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory; import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; 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.internal.ui.viewers.model.provisional.IModelProxy; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicyFactory; import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputProvider; import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputUpdate; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.debug.ui.IDebugView; import org.eclipse.debug.ui.ISourcePresentation; import org.eclipse.debug.ui.contexts.ISuspendTrigger; import org.eclipse.debug.ui.contexts.ISuspendTriggerListener; import org.eclipse.debug.ui.sourcelookup.CommonSourceNotFoundEditorInput; import org.eclipse.debug.ui.sourcelookup.ISourceDisplay; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.BusyIndicator; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.MessageBox; import org.eclipse.swt.widgets.Shell; import org.eclipse.tm.internal.tcf.debug.actions.TCFAction; import org.eclipse.tm.internal.tcf.debug.launch.TCFSourceLookupDirector; import org.eclipse.tm.internal.tcf.debug.launch.TCFSourceLookupParticipant; import org.eclipse.tm.internal.tcf.debug.model.ITCFConstants; import org.eclipse.tm.internal.tcf.debug.model.TCFContextState; import org.eclipse.tm.internal.tcf.debug.model.TCFLaunch; import org.eclipse.tm.internal.tcf.debug.model.TCFSourceRef; import org.eclipse.tm.internal.tcf.debug.ui.Activator; import org.eclipse.tm.internal.tcf.debug.ui.commands.BackIntoCommand; import org.eclipse.tm.internal.tcf.debug.ui.commands.BackOverCommand; import org.eclipse.tm.internal.tcf.debug.ui.commands.BackResumeCommand; import org.eclipse.tm.internal.tcf.debug.ui.commands.BackReturnCommand; import org.eclipse.tm.internal.tcf.debug.ui.commands.DisconnectCommand; import org.eclipse.tm.internal.tcf.debug.ui.commands.DropToFrameCommand; import org.eclipse.tm.internal.tcf.debug.ui.commands.ResumeCommand; import org.eclipse.tm.internal.tcf.debug.ui.commands.StepIntoCommand; import org.eclipse.tm.internal.tcf.debug.ui.commands.StepOverCommand; import org.eclipse.tm.internal.tcf.debug.ui.commands.StepReturnCommand; import org.eclipse.tm.internal.tcf.debug.ui.commands.SuspendCommand; import org.eclipse.tm.internal.tcf.debug.ui.commands.TerminateCommand; import org.eclipse.tm.internal.tcf.debug.ui.preferences.TCFPreferences; import org.eclipse.tm.tcf.core.Command; import org.eclipse.tm.tcf.protocol.IChannel; import org.eclipse.tm.tcf.protocol.IErrorReport; import org.eclipse.tm.tcf.protocol.IService; import org.eclipse.tm.tcf.protocol.IToken; import org.eclipse.tm.tcf.protocol.Protocol; import org.eclipse.tm.tcf.services.IDisassembly; import org.eclipse.tm.tcf.services.ILineNumbers; import org.eclipse.tm.tcf.services.IMemory; import org.eclipse.tm.tcf.services.IMemoryMap; import org.eclipse.tm.tcf.services.IProcesses; import org.eclipse.tm.tcf.services.IRegisters; import org.eclipse.tm.tcf.services.IRunControl; import org.eclipse.tm.tcf.services.IStackTrace; import org.eclipse.tm.tcf.services.ISymbols; import org.eclipse.tm.tcf.util.TCFDataCache; import org.eclipse.tm.tcf.util.TCFTask; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IPersistableElement; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.texteditor.ITextEditor; /** * TCFModel represents remote target state as it is known to host. * The main job of the model is caching remote data, * keeping the cache in a coherent state, * and feeding UI with up-to-date data. */ @SuppressWarnings("restriction") public class TCFModel implements IElementContentProvider, IElementLabelProvider, IViewerInputProvider, IModelProxyFactory, IColumnPresentationFactory, ISourceDisplay, ISuspendTrigger { /** The id of the expression hover presentation context */ public static final String ID_EXPRESSION_HOVER = Activator.PLUGIN_ID + ".expression_hover"; /** The id of a pinned view description presentation context */ public static final String ID_PINNED_VIEW = Activator.PLUGIN_ID + ".pinned_view"; public static final int UPDATE_POLICY_AUTOMATIC = 0, UPDATE_POLICY_MANUAL = 1, UPDATE_POLICY_BREAKPOINT = 2; /** * A dummy editor input to open the disassembly view as editor. */ public static class DisassemblyEditorInput implements IEditorInput { final static String EDITOR_ID = "org.eclipse.cdt.dsf.ui.disassembly"; final static DisassemblyEditorInput INSTANCE = new DisassemblyEditorInput(); @SuppressWarnings("rawtypes") public Object getAdapter(Class adapter) { return null; } public boolean exists() { return false; } public ImageDescriptor getImageDescriptor() { return null; } public String getName() { return "Disassembly"; } public IPersistableElement getPersistable() { return null; } public String getToolTipText() { return "Disassembly"; } } private final TCFLaunch launch; private final Display display; private final IExpressionManager expr_manager; private final TCFAnnotationManager annotation_manager; private final List<ISuspendTriggerListener> suspend_trigger_listeners = new LinkedList<ISuspendTriggerListener>(); private int display_source_generation; private int suspend_trigger_generation; private int auto_disconnect_generation; // Debugger preferences: private long min_view_updates_interval; private boolean view_updates_throttle_enabled; private boolean channel_throttle_enabled; private boolean wait_for_pc_update_after_step; private boolean wait_for_views_update_after_step; private boolean delay_stack_update_until_last_step; private boolean stack_frames_limit_enabled; private int stack_frames_limit_value; private boolean show_function_arg_names; private boolean show_function_arg_values; private boolean delay_children_list_updates; private boolean auto_children_list_updates; private final Map<String,String> action_results = new HashMap<String,String>(); private final HashMap<String,TCFAction> active_actions = new HashMap<String,TCFAction>(); private final Map<IPresentationContext,TCFModelProxy> model_proxies = new HashMap<IPresentationContext,TCFModelProxy>(); private final Map<String,TCFNode> id2node = new HashMap<String,TCFNode>(); private final Map<Class<?>,Object> adapters = new HashMap<Class<?>,Object>(); private class MemoryBlocksUpdate extends TCFDataCache<Map<String,TCFMemoryBlockRetrieval>> { final Set<String> changeset = new HashSet<String>(); final Set<String> suspended = new HashSet<String>(); MemoryBlocksUpdate(IChannel channel) { super(channel); Protocol.invokeLater(new Runnable() { public void run() { if (!validate(this)) return; Map<String,TCFMemoryBlockRetrieval> map = getData(); if (map != null) { // map can be null if, for example, the channel was closed for (TCFMemoryBlockRetrieval r : map.values()) { r.onMemoryChanged(suspended.contains(r.getMemoryID())); } } launch.removePendingClient(mem_blocks_update); mem_blocks_update = null; } }); } void add(String id, boolean suspended) { changeset.add(id); if (suspended) this.suspended.add(id); } public boolean startDataRetrieval() { // Map changed contexts to memory nodes, and then to memory block retrieval objects Map<String,TCFMemoryBlockRetrieval> map = new HashMap<String,TCFMemoryBlockRetrieval>(); for (String id : changeset) { if (map.get(id) != null) continue; TCFNode node = id2node.get(id); if (node == null) { if (!createNode(id, this)) return false; if (isValid()) { Activator.log("Cannot create debug model node", getError()); reset(); continue; } node = id2node.get(id); } if (node instanceof TCFNodeExecContext) { TCFDataCache<TCFNodeExecContext> c = ((TCFNodeExecContext)node).getMemoryNode(); if (!c.validate(this)) return false; node = c.getData(); if (node == null) continue; TCFMemoryBlockRetrieval r = mem_retrieval.get(node.id); if (r != null) { map.put(node.id, r); if (suspended.contains(id)) suspended.add(node.id); } } } set(null, null, map); return true; } } private final Map<String,TCFMemoryBlockRetrieval> mem_retrieval = new HashMap<String,TCFMemoryBlockRetrieval>(); private MemoryBlocksUpdate mem_blocks_update; private final Map<String,String> cast_to_type_map = new HashMap<String,String>(); private final Map<String,Object> context_map = new HashMap<String,Object>(); private final Set<String> expanded_nodes = new HashSet<String>(); private final Map<IWorkbenchPart,TCFNode> pins = new HashMap<IWorkbenchPart,TCFNode>(); private final Map<IWorkbenchPart,TCFSnapshot> locks = new HashMap<IWorkbenchPart,TCFSnapshot>(); private final Map<IWorkbenchPart,Integer> lock_policy = new HashMap<IWorkbenchPart,Integer>(); private TCFConsole console; private static final Map<ILaunchConfiguration,IEditorInput> editor_not_found = new HashMap<ILaunchConfiguration,IEditorInput>(); private final IModelSelectionPolicyFactory model_selection_factory = new IModelSelectionPolicyFactory() { public IModelSelectionPolicy createModelSelectionPolicyAdapter( Object element, IPresentationContext context) { return selection_policy; } }; private final IModelSelectionPolicy selection_policy; private IChannel channel; private TCFNodeLaunch launch_node; private boolean disposed; private final IMemory.MemoryListener mem_listener = new IMemory.MemoryListener() { public void contextAdded(IMemory.MemoryContext[] contexts) { for (IMemory.MemoryContext ctx : contexts) { String id = ctx.getParentID(); if (id == null) { launch_node.onContextAdded(ctx); } else { TCFNode node = getNode(id); if (node instanceof TCFNodeExecContext) { ((TCFNodeExecContext)node).onContextAdded(ctx); } } } launch_node.onAnyContextAddedOrRemoved(); } public void contextChanged(IMemory.MemoryContext[] contexts) { for (IMemory.MemoryContext ctx : contexts) { String id = ctx.getID(); TCFNode node = getNode(id); if (node instanceof TCFNodeExecContext) { ((TCFNodeExecContext)node).onContextChanged(ctx); } onMemoryChanged(id, true, false); } } public void contextRemoved(final String[] context_ids) { onContextRemoved(context_ids); } public void memoryChanged(String id, Number[] addr, long[] size) { TCFNode node = getNode(id); if (node instanceof TCFNodeExecContext) { ((TCFNodeExecContext)node).onMemoryChanged(addr, size); } onMemoryChanged(id, true, false); } }; private final IRunControl.RunControlListener run_listener = new IRunControl.RunControlListener() { public void containerResumed(String[] context_ids) { for (String id : context_ids) { TCFNode node = getNode(id); if (node instanceof TCFNodeExecContext) { ((TCFNodeExecContext)node).onContainerResumed(); } } annotation_manager.updateAnnotations(null, launch); } public void containerSuspended(String context, String pc, String reason, Map<String,Object> params, String[] suspended_ids) { int action_cnt = 0; for (String id : suspended_ids) { TCFNode node = getNode(id); action_results.remove(id); if (active_actions.get(id) != null) action_cnt++; if (!id.equals(context) && node instanceof TCFNodeExecContext) { ((TCFNodeExecContext)node).onContainerSuspended(); } onMemoryChanged(id, false, true); } TCFNode node = getNode(context); if (node instanceof TCFNodeExecContext) { ((TCFNodeExecContext)node).onContextSuspended(pc, reason, params); } launch_node.onAnyContextSuspendedOrChanged(); if (action_cnt == 0) { setDebugViewSelection(node, reason); annotation_manager.updateAnnotations(null, launch); } action_results.remove(context); } public void contextAdded(IRunControl.RunControlContext[] contexts) { for (IRunControl.RunControlContext ctx : contexts) { String id = ctx.getParentID(); if (id == null) { launch_node.onContextAdded(ctx); } else { TCFNode node = getNode(id); if (node instanceof TCFNodeExecContext) { ((TCFNodeExecContext)node).onContextAdded(ctx); } } context_map.put(ctx.getID(), ctx); } launch_node.onAnyContextAddedOrRemoved(); } public void contextChanged(IRunControl.RunControlContext[] contexts) { for (IRunControl.RunControlContext ctx : contexts) { String id = ctx.getID(); context_map.put(id, ctx); TCFNode node = getNode(id); if (node instanceof TCFNodeExecContext) { ((TCFNodeExecContext)node).onContextChanged(ctx); } onMemoryChanged(id, true, false); } launch_node.onAnyContextSuspendedOrChanged(); } public void contextException(String context, String msg) { TCFNode node = getNode(context); if (node instanceof TCFNodeExecContext) { ((TCFNodeExecContext)node).onContextException(msg); } } public void contextRemoved(final String[] context_ids) { onContextRemoved(context_ids); } public void contextResumed(String id) { TCFNode node = getNode(id); if (node instanceof TCFNodeExecContext) { ((TCFNodeExecContext)node).onContextResumed(); } annotation_manager.updateAnnotations(null, launch); } public void contextSuspended(String id, String pc, String reason, Map<String,Object> params) { TCFNode node = getNode(id); action_results.remove(id); if (node instanceof TCFNodeExecContext) { ((TCFNodeExecContext)node).onContextSuspended(pc, reason, params); } launch_node.onAnyContextSuspendedOrChanged(); if (active_actions.get(id) == null) { setDebugViewSelection(node, reason); annotation_manager.updateAnnotations(null, launch); } onMemoryChanged(id, false, true); } }; private final IMemoryMap.MemoryMapListener mmap_listenr = new IMemoryMap.MemoryMapListener() { public void changed(String id) { TCFNode node = getNode(id); if (node instanceof TCFNodeExecContext) { TCFNodeExecContext exe = (TCFNodeExecContext)node; exe.onMemoryMapChanged(); } onMemoryChanged(id, true, false); display.asyncExec(new Runnable() { public void run() { if (PlatformUI.isWorkbenchRunning()) { for (IWorkbenchWindow window : PlatformUI.getWorkbench().getWorkbenchWindows()) { IWorkbenchPage page = window.getActivePage(); if (page != null) displaySource(null, page, true); } } } }); } }; private final IRegisters.RegistersListener reg_listener = new IRegisters.RegistersListener() { public void contextChanged() { for (TCFNode node : id2node.values()) { if (node instanceof TCFNodeExecContext) { ((TCFNodeExecContext)node).onRegistersChanged(); } } } public void registerChanged(String context) { TCFNode node = getNode(context); if (node instanceof TCFNodeRegister) { ((TCFNodeRegister)node).onValueChanged(); } } }; private final IProcesses.ProcessesListener prs_listener = new IProcesses.ProcessesListener() { public void exited(String process_id, int exit_code) { IProcesses.ProcessContext prs = launch.getProcessContext(); if (prs != null && process_id.equals(prs.getID())) onContextOrProcessRemoved(); } }; private final IExpressionsListener expressions_listener = new IExpressionsListener() { int generation; public void expressionsAdded(IExpression[] expressions) { expressionsRemoved(expressions); } public void expressionsChanged(IExpression[] expressions) { expressionsRemoved(expressions); } public void expressionsRemoved(IExpression[] expressions) { final int g = ++generation; Protocol.invokeLater(new Runnable() { public void run() { if (g != generation) return; for (TCFNode n : id2node.values()) { if (n instanceof TCFNodeExecContext) { ((TCFNodeExecContext)n).onExpressionAddedOrRemoved(); } } for (TCFModelProxy p : model_proxies.values()) { String id = p.getPresentationContext().getId(); if (IDebugUIConstants.ID_EXPRESSION_VIEW.equals(id)) { Object o = p.getInput(); if (o instanceof TCFNode) { TCFNode n = (TCFNode)o; if (n.model == TCFModel.this) p.addDelta(n, IModelDelta.CONTENT); } } } } }); } }; private final TCFLaunch.ActionsListener actions_listener = new TCFLaunch.ActionsListener() { public void onContextActionStart(TCFAction action) { final String id = action.getContextID(); active_actions.put(id, action); annotation_manager.updateAnnotations(null, launch); } public void onContextActionResult(String id, String reason) { if (reason == null) action_results.remove(id); else action_results.put(id, reason); } public void onContextActionDone(TCFAction action) { String id = action.getContextID(); active_actions.remove(id); TCFNode node = getNode(id); if (node instanceof TCFNodeExecContext) { ((TCFNodeExecContext)node).onContextActionDone(); } setDebugViewSelection(id2node.get(id), "Action"); for (TCFModelProxy p : model_proxies.values()) p.post(); annotation_manager.updateAnnotations(null, launch); } }; private final IDebugModelProvider debug_model_provider = new IDebugModelProvider() { public String[] getModelIdentifiers() { return new String[] { ITCFConstants.ID_TCF_DEBUG_MODEL }; } }; private class InitialSelection implements Runnable { boolean done; public void run() { if (done) return; ArrayList<TCFNodeExecContext> nodes = new ArrayList<TCFNodeExecContext>(); if (!searchSuspendedThreads(launch_node.getFilteredChildren(), nodes)) return; if (nodes.size() == 0) { setDebugViewSelection(launch_node, "Launch"); } else if (nodes.size() == 1) { TCFNodeExecContext n = nodes.get(0); setDebugViewSelection(n, "Launch"); } else { for (TCFNodeExecContext n : nodes) { String reason = n.getState().getData().suspend_reason; setDebugViewSelection(n, reason); } } done = true; } private boolean searchSuspendedThreads(TCFChildren c, ArrayList<TCFNodeExecContext> nodes) { if (!c.validate(this)) return false; for (TCFNode n : c.toArray()) { if (!searchSuspendedThreads((TCFNodeExecContext)n, nodes)) return false; } return true; } private boolean searchSuspendedThreads(TCFNodeExecContext n, ArrayList<TCFNodeExecContext> nodes) { TCFDataCache<IRunControl.RunControlContext> run_context = n.getRunContext(); if (!run_context.validate(this)) return false; IRunControl.RunControlContext ctx = run_context.getData(); if (ctx != null && ctx.hasState()) { TCFDataCache<TCFContextState> state = n.getState(); if (!state.validate(this)) return false; TCFContextState s = state.getData(); if (s != null && s.is_suspended) nodes.add(n); return true; } return searchSuspendedThreads(n.getChildren(), nodes); } } private volatile boolean instruction_stepping_enabled; TCFModel(final TCFLaunch launch) { this.launch = launch; display = PlatformUI.getWorkbench().getDisplay(); selection_policy = new TCFModelSelectionPolicy(this); adapters.put(ILaunch.class, launch); adapters.put(IModelSelectionPolicy.class, selection_policy); adapters.put(IModelSelectionPolicyFactory.class, model_selection_factory); adapters.put(IDebugModelProvider.class, debug_model_provider); adapters.put(ISuspendHandler.class, new SuspendCommand(this)); adapters.put(IResumeHandler.class, new ResumeCommand(this)); adapters.put(BackResumeCommand.class, new BackResumeCommand(this)); adapters.put(ITerminateHandler.class, new TerminateCommand(this)); adapters.put(IDisconnectHandler.class, new DisconnectCommand(this)); adapters.put(IStepIntoHandler.class, new StepIntoCommand(this)); adapters.put(IStepOverHandler.class, new StepOverCommand(this)); adapters.put(IStepReturnHandler.class, new StepReturnCommand(this)); adapters.put(BackIntoCommand.class, new BackIntoCommand(this)); adapters.put(BackOverCommand.class, new BackOverCommand(this)); adapters.put(BackReturnCommand.class, new BackReturnCommand(this)); adapters.put(IDropToFrameHandler.class, new DropToFrameCommand(this)); expr_manager = DebugPlugin.getDefault().getExpressionManager(); expr_manager.addExpressionListener(expressions_listener); annotation_manager = Activator.getAnnotationManager(); launch.addActionsListener(actions_listener); final IPreferenceStore prefs = TCFPreferences.getPreferenceStore(); IPropertyChangeListener listener = new IPropertyChangeListener() { public void propertyChange(PropertyChangeEvent event) { launch.setContextActionsInterval(prefs.getLong(TCFPreferences.PREF_MIN_STEP_INTERVAL)); min_view_updates_interval = prefs.getLong(TCFPreferences.PREF_MIN_UPDATE_INTERVAL); view_updates_throttle_enabled = prefs.getBoolean(TCFPreferences.PREF_VIEW_UPDATES_THROTTLE); channel_throttle_enabled = prefs.getBoolean(TCFPreferences.PREF_TARGET_TRAFFIC_THROTTLE); wait_for_pc_update_after_step = prefs.getBoolean(TCFPreferences.PREF_WAIT_FOR_PC_UPDATE_AFTER_STEP); wait_for_views_update_after_step = prefs.getBoolean(TCFPreferences.PREF_WAIT_FOR_VIEWS_UPDATE_AFTER_STEP); delay_stack_update_until_last_step = prefs.getBoolean(TCFPreferences.PREF_DELAY_STACK_UPDATE_UNTIL_LAST_STEP); stack_frames_limit_enabled = prefs.getBoolean(TCFPreferences.PREF_STACK_FRAME_LIMIT_ENABLED); stack_frames_limit_value = prefs.getInt(TCFPreferences.PREF_STACK_FRAME_LIMIT_VALUE); show_function_arg_names = prefs.getBoolean(TCFPreferences.PREF_STACK_FRAME_ARG_NAMES); show_function_arg_values = prefs.getBoolean(TCFPreferences.PREF_STACK_FRAME_ARG_VALUES); auto_children_list_updates = prefs.getBoolean(TCFPreferences.PREF_AUTO_CHILDREN_LIST_UPDATES); delay_children_list_updates = prefs.getBoolean(TCFPreferences.PREF_DELAY_CHILDREN_LIST_UPDATES); Protocol.invokeLater(new Runnable() { public void run() { for (TCFNode n : id2node.values()) { if (n instanceof TCFNodeExecContext) { ((TCFNodeExecContext)n).onPreferencesChanged(); } } } }); } }; listener.propertyChange(null); prefs.addPropertyChangeListener(listener); } /** * Add an adapter for given type. * * @param adapterType the type the adapter implements * @param adapter the adapter implementing <code>adapterType</code> */ public void setAdapter(Class<?> adapterType, Object adapter) { synchronized (adapters) { assert adapterType.isInstance(adapter); adapters.put(adapterType, adapter); } } @SuppressWarnings("rawtypes") public Object getAdapter(final Class adapter, final TCFNode node) { synchronized (adapters) { Object o = adapters.get(adapter); if (o != null) return o; } if (adapter == IMemoryBlockRetrieval.class || adapter == IMemoryBlockRetrievalExtension.class) { return new TCFTask<Object>() { public void run() { Object o = null; TCFDataCache<TCFNodeExecContext> cache = searchMemoryContext(node); if (cache != null) { if (!cache.validate(this)) return; if (cache.getData() != null) { TCFNodeExecContext ctx = cache.getData(); o = mem_retrieval.get(ctx.id); if (o == null) { TCFMemoryBlockRetrieval m = new TCFMemoryBlockRetrieval(ctx); mem_retrieval.put(ctx.id, m); o = m; } } } assert o == null || adapter.isInstance(o); done(o); } }.getE(); } return null; } void onConnected() { assert Protocol.isDispatchThread(); assert launch_node == null; channel = launch.getChannel(); launch_node = new TCFNodeLaunch(this); IMemory mem = launch.getService(IMemory.class); if (mem != null) mem.addListener(mem_listener); IRunControl run = launch.getService(IRunControl.class); if (run != null) run.addListener(run_listener); IMemoryMap mmap = launch.getService(IMemoryMap.class); if (mmap != null) mmap.addListener(mmap_listenr); IRegisters reg = launch.getService(IRegisters.class); if (reg != null) reg.addListener(reg_listener); IProcesses prs = launch.getService(IProcesses.class); if (prs != null) prs.addListener(prs_listener); launchChanged(); for (TCFModelProxy p : model_proxies.values()) { String id = p.getPresentationContext().getId(); if (IDebugUIConstants.ID_DEBUG_VIEW.equals(id)) { Protocol.invokeLater(new InitialSelection()); } } } void onDisconnected() { assert Protocol.isDispatchThread(); if (locks.size() > 0) { TCFSnapshot[] arr = locks.values().toArray(new TCFSnapshot[locks.size()]); locks.clear(); for (TCFSnapshot s : arr) s.dispose(); } if (launch_node != null) { launch_node.dispose(); launch_node = null; } refreshLaunchView(); assert id2node.size() == 0; } void onProcessOutput(String process_id, final int stream_id, byte[] data) { IProcesses.ProcessContext prs = launch.getProcessContext(); if (prs == null || !process_id.equals(prs.getID())) return; if (console == null) console = new TCFConsole(this, process_id); console.write(stream_id, data); } void onProcessStreamError(String process_id, int stream_id, Exception x, int lost_size) { if (channel != null && channel.getState() == IChannel.STATE_CLOSED) return; StringBuffer bf = new StringBuffer(); bf.append("Debugger console IO error"); if (process_id != null) { bf.append(". Process ID "); bf.append(process_id); } bf.append(". Stream "); bf.append(stream_id); if (lost_size > 0) { bf.append(". Lost data size "); bf.append(lost_size); } Activator.log(bf.toString(), x); } void onMemoryChanged(String id, boolean notify_references, boolean context_suspended) { if (channel == null) return; if (notify_references) { for (Object obj : context_map.values()) { if (obj instanceof IRunControl.RunControlContext) { IRunControl.RunControlContext subctx = (IRunControl.RunControlContext)obj; if (id.equals(subctx.getProcessID()) && !id.equals(subctx.getID())) { TCFNode subnode = getNode(subctx.getID()); if (subnode instanceof TCFNodeExecContext) { ((TCFNodeExecContext)subnode).onMemoryChanged(null, null); } } } } } if (mem_retrieval.size() == 0) return; if (mem_blocks_update == null) { mem_blocks_update = new MemoryBlocksUpdate(channel); if (wait_for_views_update_after_step) { launch.addPendingClient(mem_blocks_update); } } mem_blocks_update.add(id, context_suspended); } public TCFAction getActiveAction(String id) { return active_actions.get(id); } String getContextActionResult(String id) { return action_results.get(id); } public long getMinViewUpdatesInterval() { return min_view_updates_interval; } public boolean getViewUpdatesThrottleEnabled() { return view_updates_throttle_enabled; } public boolean getWaitForViewsUpdateAfterStep() { return wait_for_views_update_after_step; } public boolean getDelayStackUpdateUtilLastStep() { return delay_stack_update_until_last_step; } public boolean getChannelThrottleEnabled() { return channel_throttle_enabled; } public boolean getStackFramesLimitEnabled() { return stack_frames_limit_enabled; } public int getStackFramesLimitValue() { return stack_frames_limit_value; } public boolean getShowFunctionArgNames() { return show_function_arg_names; } public boolean getShowFunctionArgValues() { return show_function_arg_values; } public boolean getAutoChildrenListUpdates() { return auto_children_list_updates; } public boolean getDelayChildrenListUpdates() { return delay_children_list_updates; } void onProxyInstalled(TCFModelProxy mp) { IPresentationContext pc = mp.getPresentationContext(); model_proxies.put(mp.getPresentationContext(), mp); if (launch_node != null && pc.getId().equals(IDebugUIConstants.ID_DEBUG_VIEW)) { Protocol.invokeLater(new InitialSelection()); } } void onProxyDisposed(TCFModelProxy mp) { IPresentationContext ctx = mp.getPresentationContext(); assert model_proxies.get(ctx) == mp; model_proxies.remove(ctx); } private void onContextRemoved(String[] context_ids) { for (String id : context_ids) { TCFNode node = getNode(id); if (node instanceof TCFNodeExecContext) { ((TCFNodeExecContext)node).onContextRemoved(); } action_results.remove(id); context_map.remove(id); expanded_nodes.remove(id); if (mem_blocks_update != null) mem_blocks_update.changeset.remove(id); } launch_node.onAnyContextAddedOrRemoved(); // Close debug session if the last context is removed: onContextOrProcessRemoved(); annotation_manager.updateAnnotations(null, launch); } void onContextRunning() { annotation_manager.updateAnnotations(null, launch); } void updateContextMap(String id, IRunControl.RunControlContext ctx) { context_map.put(id, ctx); } private void onContextOrProcessRemoved() { final int generation = ++auto_disconnect_generation; Protocol.invokeLater(1000, new Runnable() { public void run() { if (generation != auto_disconnect_generation) return; if (launch_node == null) return; if (launch_node.isDisposed()) return; TCFChildren children = launch_node.getFilteredChildren(); if (!children.validate(this)) return; if (children.size() > 0) return; launch.onLastContextRemoved(); } }); } void launchChanged() { if (launch_node != null) { for (TCFModelProxy p : model_proxies.values()) { String id = p.getPresentationContext().getId(); if (IDebugUIConstants.ID_DEBUG_VIEW.equals(id)) { p.addDelta(launch_node, IModelDelta.STATE | IModelDelta.CONTENT); } } } else { refreshLaunchView(); } } Collection<TCFModelProxy> getModelProxies() { return model_proxies.values(); } void dispose() { launch.removeActionsListener(actions_listener); expr_manager.removeExpressionListener(expressions_listener); if (console != null) console.close(); } void addNode(String id, TCFNode node) { assert id != null; assert Protocol.isDispatchThread(); assert id2node.get(id) == null; assert !node.isDisposed(); id2node.put(id, node); } void removeNode(String id) { assert id != null; assert Protocol.isDispatchThread(); id2node.remove(id); mem_retrieval.remove(id); } void flushAllCaches() { for (TCFNode n : id2node.values()) n.flushAllCaches(); } public IExpressionManager getExpressionManager() { return expr_manager; } public Display getDisplay() { return display; } /** * @return debug model launch object. */ public TCFLaunch getLaunch() { return launch; } /** * @return communication channel that this model is using. */ public IChannel getChannel() { return channel; } /** * Get top level (root) debug model node. * Same as getNode(""). * @return root node. */ public TCFNodeLaunch getRootNode() { return launch_node; } /** * Set current hover expression for a given model node, * and return a cache of expression nodes that represents given expression. * The model allows only one current hover expression per node at any time, * however it will cache results of recent expression evaluations, * and it will re-use cached results when current hover expression changes. * The cache getData() method should not return more then 1 node, * and it can return an empty collection. * @param parent - a thread or stack frame where the expression should be evaluated. * @param expression - the expression text, can be null. * @return a cache of expression nodes. */ public TCFChildren getHoverExpressionCache(TCFNode parent, String expression) { assert Protocol.isDispatchThread(); if (parent instanceof TCFNodeStackFrame) { return ((TCFNodeStackFrame)parent).getHoverExpressionCache(expression); } if (parent instanceof TCFNodeExecContext) { return ((TCFNodeExecContext)parent).getHoverExpressionCache(expression); } return null; } /** * Get a model node with given ID. * ID == "" means launch node. * @param id - node ID. * @return debug model node or null if no node exists with such ID. */ public TCFNode getNode(String id) { if (id == null) return null; if (id.equals("")) return launch_node; assert Protocol.isDispatchThread(); return id2node.get(id); } /** * Get a type that should be used to cast a value of an expression when it is shown in a view. * Return null if original type of the value should be used. * @param id - expression node ID. * @return a string that designates a type or null. */ public String getCastToType(String id) { return cast_to_type_map.get(id); } /** * Register a type that should be used to cast a value of an expression when it is shown in a view. * 'type' == null means original type of the value should be used. * @param id - expression node ID. * @param type - a string that designates a type. */ public void setCastToType(String id, String type) { if (type != null && type.trim().length() == 0) type = null; if (type == null) cast_to_type_map.remove(id); else cast_to_type_map.put(id, type); TCFNode node = id2node.get(id); if (node instanceof ICastToType) { ((ICastToType)node).onCastToTypeChanged(); } } /** * Get a data cache that contains properties of a symbol. * New cache object is created if it does not exist yet. * @param sym_id - the symbol ID. * @return data cache object. */ public TCFDataCache<ISymbols.Symbol> getSymbolInfoCache(final String sym_id) { if (sym_id == null) return null; TCFNodeSymbol n = (TCFNodeSymbol)getNode(sym_id); if (n == null) n = new TCFNodeSymbol(launch_node, sym_id); return n.getContext(); } /** * Get a data cache that contains children of a symbol. * New cache object is created if it does not exist yet. * @param sym_id - the symbol ID. * @return data cache object. */ public TCFDataCache<String[]> getSymbolChildrenCache(final String sym_id) { if (sym_id == null) return null; TCFNodeSymbol n = (TCFNodeSymbol)getNode(sym_id); if (n == null) n = new TCFNodeSymbol(launch_node, sym_id); return n.getChildren(); } /** * Search memory context that owns the object represented by given node. * @return data cache item that holds the memory context node. */ public TCFDataCache<TCFNodeExecContext> searchMemoryContext(final TCFNode node) { TCFNode n = node; while (n != null && !n.isDisposed()) { if (n instanceof TCFNodeExecContext) return ((TCFNodeExecContext)n).getMemoryNode(); n = n.parent; } return null; } /** * Asynchronously create model node for given ID. * If 'cache' is valid after the method returns, the node cannot be created, * and the cache will contain an error report. * @param id - context ID. * @param cache - data cache object that need the node for validation. * @return - true if all done, false if 'cache' is waiting for remote data. */ public boolean createNode(String id, final TCFDataCache<?> cache) { TCFNode parent = getNode(id); if (parent != null) return true; LinkedList<Object> path = null; for (;;) { Object obj = context_map.get(id); if (obj == null) obj = new CreateNodeRunnable(id); if (obj instanceof CreateNodeRunnable) { ((CreateNodeRunnable)obj).wait(cache); return false; } if (obj instanceof Throwable) { cache.set(null, (Throwable)obj, null); return true; } if (path == null) path = new LinkedList<Object>(); path.add(obj); String parent_id = null; if (obj instanceof IRunControl.RunControlContext) { parent_id = ((IRunControl.RunControlContext)obj).getParentID(); } else if (obj instanceof IStackTrace.StackTraceContext) { parent_id = ((IStackTrace.StackTraceContext)obj).getParentID(); } else { parent_id = ((IRegisters.RegistersContext)obj).getParentID(); } parent = parent_id == null ? launch_node : getNode(parent_id); if (parent != null) break; id = parent_id; } while (path.size() > 0) { Object obj = path.removeLast(); if (obj instanceof IRunControl.RunControlContext) { IRunControl.RunControlContext ctx = (IRunControl.RunControlContext)obj; TCFNodeExecContext n = new TCFNodeExecContext(parent, ctx.getID()); if (parent instanceof TCFNodeLaunch) ((TCFNodeLaunch)parent).getChildren().add(n); else ((TCFNodeExecContext)parent).getChildren().add(n); n.setRunContext(ctx); parent = n; } else if (obj instanceof IStackTrace.StackTraceContext) { IStackTrace.StackTraceContext ctx = (IStackTrace.StackTraceContext)obj; TCFNodeStackFrame n = new TCFNodeStackFrame((TCFNodeExecContext)parent, ctx.getID(), false); ((TCFNodeExecContext)parent).getStackTrace().add(n); parent = n; } else if (obj instanceof IRegisters.RegistersContext) { IRegisters.RegistersContext ctx = (IRegisters.RegistersContext)obj; TCFNodeRegister n = new TCFNodeRegister(parent, ctx.getID()); if (parent instanceof TCFNodeRegister) ((TCFNodeRegister)parent).getChildren().add(n); else if (parent instanceof TCFNodeStackFrame) ((TCFNodeStackFrame)parent).getRegisters().add(n); else ((TCFNodeExecContext)parent).getRegisters().add(n); parent = n; } else { assert false; } } return true; } private class CreateNodeRunnable implements Runnable { final String id; final ArrayList<Runnable> waiting_list = new ArrayList<Runnable>(); final ArrayList<IService> service_list = new ArrayList<IService>(); CreateNodeRunnable(String id) { this.id = id; assert context_map.get(id) == null; String[] arr = { IRunControl.NAME, IStackTrace.NAME, IRegisters.NAME }; for (String nm : arr) { IService s = channel.getRemoteService(nm); if (s != null) service_list.add(s); } context_map.put(id, this); Protocol.invokeLater(this); } void wait(Runnable r) { assert context_map.get(id) == this; waiting_list.add(r); } public void run() { assert context_map.get(id) == this; if (service_list.size() == 0) { context_map.put(id, new Exception("Invalid context ID")); for (Runnable r : waiting_list) r.run(); } else { IService s = service_list.remove(0); if (s instanceof IRunControl) { ((IRunControl)s).getContext(id, new IRunControl.DoneGetContext() { public void doneGetContext(IToken token, Exception error, IRunControl.RunControlContext context) { if (error == null && context != null) { context_map.put(id, context); for (Runnable r : waiting_list) r.run(); } else { run(); } } }); } else if (s instanceof IStackTrace) { ((IStackTrace)s).getContext(new String[]{ id }, new IStackTrace.DoneGetContext() { public void doneGetContext(IToken token, Exception error, IStackTrace.StackTraceContext[] context) { if (error == null && context != null && context.length == 1 && context[0] != null) { context_map.put(id, context[0]); for (Runnable r : waiting_list) r.run(); } else { run(); } } }); } else { ((IRegisters)s).getContext(id, new IRegisters.DoneGetContext() { public void doneGetContext(IToken token, Exception error, IRegisters.RegistersContext context) { if (error == null && context != null) { context_map.put(id, context); for (Runnable r : waiting_list) r.run(); } else { run(); } } }); } } } } public void update(IChildrenCountUpdate[] updates) { for (IChildrenCountUpdate update : updates) { Object o = update.getElement(); if (o instanceof TCFLaunch) { if (launch_node != null) { launch_node.update(update); } else { update.setChildCount(0); update.done(); } } else { ((TCFNode)o).update(update); } } } public void update(IChildrenUpdate[] updates) { for (IChildrenUpdate update : updates) { Object o = update.getElement(); if (o instanceof TCFLaunch) { if (launch_node != null) { launch_node.update(update); } else { update.done(); } } else { ((TCFNode)o).update(update); } } } public void update(IHasChildrenUpdate[] updates) { for (IHasChildrenUpdate update : updates) { Object o = update.getElement(); if (o instanceof TCFLaunch) { if (launch_node != null) { launch_node.update(update); } else { update.setHasChilren(false); update.done(); } } else { ((TCFNode)o).update(update); } } } public void update(ILabelUpdate[] updates) { for (ILabelUpdate update : updates) { Object o = update.getElement(); // Launch label is provided by TCFLaunchLabelProvider class. assert !(o instanceof TCFLaunch); ((TCFNode)o).update(update); } } public void update(final IViewerInputUpdate update) { Protocol.invokeLater(new Runnable() { public void run() { TCFNode node = pins.get(update.getPresentationContext().getPart()); if (node != null) { node.update(update); } else { if (IDebugUIConstants.ID_BREAKPOINT_VIEW.equals(update.getPresentationContext().getId())) { // Current implementation does not support flexible hierarchy for breakpoints IViewerInputProvider p = (IViewerInputProvider)launch.getAdapter(IViewerInputProvider.class); if (p != null) { p.update(update); return; } } Object o = update.getElement(); if (o instanceof TCFLaunch) { update.setInputElement(o); update.done(); } else { ((TCFNode)o).update(update); } } } }); } public IModelProxy createModelProxy(Object element, IPresentationContext context) { return new TCFModelProxy(this); } public IColumnPresentation createColumnPresentation(IPresentationContext context, Object element) { String id = getColumnPresentationId(context, element); if (id == null) return null; if (id.equals(TCFColumnPresentationRegister.PRESENTATION_ID)) return new TCFColumnPresentationRegister(); if (id.equals(TCFColumnPresentationExpression.PRESENTATION_ID)) return new TCFColumnPresentationExpression(); if (id.equals(TCFColumnPresentationModules.PRESENTATION_ID)) return new TCFColumnPresentationModules(); return null; } public String getColumnPresentationId(IPresentationContext context, Object element) { if (IDebugUIConstants.ID_REGISTER_VIEW.equals(context.getId())) { return TCFColumnPresentationRegister.PRESENTATION_ID; } if (IDebugUIConstants.ID_VARIABLE_VIEW.equals(context.getId())) { return TCFColumnPresentationExpression.PRESENTATION_ID; } if (IDebugUIConstants.ID_EXPRESSION_VIEW.equals(context.getId())) { return TCFColumnPresentationExpression.PRESENTATION_ID; } if (ID_EXPRESSION_HOVER.equals(context.getId())) { return TCFColumnPresentationExpression.PRESENTATION_ID; } if (IDebugUIConstants.ID_MODULE_VIEW.equals(context.getId())) { return TCFColumnPresentationModules.PRESENTATION_ID; } return null; } public void setPin(IWorkbenchPart part, TCFNode node) { assert Protocol.isDispatchThread(); if (node == null) pins.remove(part); else pins.put(part, node); } private IPresentationContext getPresentationContext(IWorkbenchPart part) { if (part instanceof IDebugView) { Viewer viewer = ((IDebugView)part).getViewer(); if (viewer instanceof ITreeModelViewer) { ITreeModelViewer t = ((ITreeModelViewer)viewer); return t.getPresentationContext(); } } return null; } public void setLock(IWorkbenchPart part) { if (launch_node == null) return; IPresentationContext ctx = getPresentationContext(part); if (ctx == null) return; locks.put(part, new TCFSnapshot(ctx)); TCFModelProxy proxy = model_proxies.get(ctx); if (proxy == null) return; proxy.addDelta((TCFNode)proxy.getInput(), IModelDelta.CONTENT); } public boolean isLocked(IWorkbenchPart part) { return locks.get(part) != null; } public boolean clearLock(IWorkbenchPart part) { TCFSnapshot snapshot = locks.remove(part); if (snapshot == null) return false; snapshot.dispose(); IPresentationContext ctx = getPresentationContext(part); if (ctx != null) { TCFModelProxy proxy = model_proxies.get(ctx); if (proxy != null) proxy.addDelta((TCFNode)proxy.getInput(), IModelDelta.CONTENT); } return true; } public void setLockPolicy(IWorkbenchPart part, int policy) { if (policy == UPDATE_POLICY_AUTOMATIC) { clearLock(part); lock_policy.remove(part); } else { if (!isLocked(part)) setLock(part); lock_policy.put(part, policy); } } public int getLockPolicy(IWorkbenchPart part) { if (locks.get(part) == null) return UPDATE_POLICY_AUTOMATIC; Integer i = lock_policy.get(part); if (i == null || i.intValue() == 0) return UPDATE_POLICY_MANUAL; return i.intValue(); } TCFSnapshot getSnapshot(IPresentationContext ctx) { return locks.get(ctx.getPart()); } public void setDebugViewSelection(TCFNode node, String reason) { assert Protocol.isDispatchThread(); if (node == null) return; if (node.isDisposed()) return; runSuspendTrigger(node); if (reason == null) return; if (reason.equals(IRunControl.REASON_USER_REQUEST)) return; for (TCFModelProxy proxy : model_proxies.values()) { if (proxy.getPresentationContext().getId().equals(IDebugUIConstants.ID_DEBUG_VIEW)) { proxy.setSelection(node); if (reason.equals(IRunControl.REASON_STEP)) continue; if (reason.equals(IRunControl.REASON_CONTAINER)) continue; if (delay_stack_update_until_last_step && launch.getContextActionsCount(node.id) != 0) continue; if (expanded_nodes.add(node.id)) proxy.expand(node); } if (reason.equals(IRunControl.REASON_BREAKPOINT)) { IWorkbenchPart part = proxy.getPresentationContext().getPart(); int policy = getLockPolicy(part); if (policy == UPDATE_POLICY_BREAKPOINT) { clearLock(part); setLock(part); } } } } /** * Reveal source code associated with given model element. * The method is part of ISourceDisplay interface. * The method is normally called from SourceLookupService. */ public void displaySource(Object model_element, final IWorkbenchPage page, boolean forceSourceLookup) { if (wait_for_pc_update_after_step) launch.addPendingClient(TCFModel.this); final int cnt = ++display_source_generation; /* Because of racing in Eclipse Debug infrastructure, 'model_element' value can be invalid. * As a workaround, get current debug view selection. */ if (page != null) { ISelection context = DebugUITools.getDebugContextManager().getContextService(page.getWorkbenchWindow()).getActiveContext(); if (context instanceof IStructuredSelection) { IStructuredSelection selection = (IStructuredSelection)context; model_element = selection.isEmpty() ? null : selection.getFirstElement(); } } final Object element = model_element; Protocol.invokeLater(25, new Runnable() { public void run() { if (cnt != display_source_generation) return; TCFNodeStackFrame stack_frame = null; if (!disposed && channel.getState() == IChannel.STATE_OPEN) { if (element instanceof TCFNodeExecContext) { TCFNodeExecContext exec_ctx = (TCFNodeExecContext)element; if (!exec_ctx.isDisposed() && active_actions.get(exec_ctx.id) == null) { TCFDataCache<TCFContextState> state_cache = exec_ctx.getState(); if (!state_cache.validate(this)) return; if (!exec_ctx.isNotActive()) { TCFContextState state_data = state_cache.getData(); if (state_data != null && state_data.is_suspended) { TCFChildrenStackTrace stack_trace = exec_ctx.getStackTrace(); if (!stack_trace.validate(this)) return; stack_frame = stack_trace.getTopFrame(); } } } } else if (element instanceof TCFNodeStackFrame) { TCFNodeStackFrame f = (TCFNodeStackFrame)element; TCFNodeExecContext exec_ctx = (TCFNodeExecContext)f.parent; if (!f.isDisposed() && !exec_ctx.isDisposed() && active_actions.get(exec_ctx.id) == null) { TCFDataCache<TCFContextState> state_cache = exec_ctx.getState(); if (!state_cache.validate(this)) return; if (!exec_ctx.isNotActive()) { TCFContextState state_data = state_cache.getData(); if (state_data != null && state_data.is_suspended) stack_frame = f; } } } } String ctx_id = null; boolean top_frame = false; ILineNumbers.CodeArea area = null; if (stack_frame != null) { TCFDataCache<TCFSourceRef> line_info = stack_frame.getLineInfo(); if (!line_info.validate(this)) return; Throwable error = line_info.getError(); TCFSourceRef src_ref = line_info.getData(); if (error == null && src_ref != null) error = src_ref.error; if (error != null) Activator.log("Error retrieving source mapping for a stack frame", error); if (src_ref != null) area = src_ref.area; top_frame = stack_frame.getFrameNo() == 0; ctx_id = stack_frame.parent.id; } displaySource(cnt, page, element, ctx_id, top_frame, area); } }); } private void displaySource(final int cnt, final IWorkbenchPage page, final Object element, final String exe_id, final boolean top_frame, final ILineNumbers.CodeArea area) { final boolean disassembly_available = channel.getRemoteService(IDisassembly.class) != null; display.asyncExec(new Runnable() { public void run() { try { if (cnt != display_source_generation) return; String editor_id = null; IEditorInput editor_input = null; int line = 0; if (area != null) { ISourceLocator locator = getLaunch().getSourceLocator(); Object source_element = TCFSourceLookupDirector.lookup(locator, area); if (source_element != null) { ISourcePresentation presentation = TCFModelPresentation.getDefault(); editor_input = presentation.getEditorInput(source_element); if (editor_input != null) editor_id = presentation.getEditorId(editor_input, source_element); line = area.start_line; } } if (area != null && !instruction_stepping_enabled && (editor_input == null || editor_id == null)) { ILaunchConfiguration cfg = launch.getLaunchConfiguration(); ISourceNotFoundPresentation presentation = (ISourceNotFoundPresentation) DebugPlugin.getAdapter(element, ISourceNotFoundPresentation.class); if (presentation != null) { String filename = TCFSourceLookupParticipant.toFileName(area); editor_input = presentation.getEditorInput(element, cfg, filename); editor_id = presentation.getEditorId(editor_input, element); } if (editor_id == null || editor_input == null) { editor_id = IDebugUIConstants.ID_COMMON_SOURCE_NOT_FOUND_EDITOR; editor_input = editor_not_found.get(cfg); if (editor_input == null) { editor_input = new CommonSourceNotFoundEditorInput(cfg); editor_not_found.put(cfg, editor_input); } } } if (exe_id != null && disassembly_available && (editor_input == null || editor_id == null || instruction_stepping_enabled) && PlatformUI.getWorkbench().getEditorRegistry().findEditor( DisassemblyEditorInput.EDITOR_ID) != null) { editor_id = DisassemblyEditorInput.EDITOR_ID; editor_input = DisassemblyEditorInput.INSTANCE; } if (cnt != display_source_generation) return; ITextEditor text_editor = null; if (page != null && editor_input != null && editor_id != null) { IEditorPart editor = openEditor(editor_input, editor_id, page); if (editor instanceof ITextEditor) { text_editor = (ITextEditor)editor; } else { text_editor = (ITextEditor)editor.getAdapter(ITextEditor.class); } } IRegion region = null; if (text_editor != null) { region = getLineInformation(text_editor, line); if (region != null) text_editor.selectAndReveal(region.getOffset(), 0); } if (wait_for_pc_update_after_step) launch.addPendingClient(annotation_manager); annotation_manager.updateAnnotations(page.getWorkbenchWindow(), launch); } finally { if (cnt == display_source_generation) launch.removePendingClient(TCFModel.this); } } }); } /* * Refresh Launch View. * Normally the view is updated by sending deltas through model proxy. * This method is used only when launch is not yet connected or already disconnected. */ private void refreshLaunchView() { // TODO: there should be a better way to refresh Launch View synchronized (Device.class) { if (display.isDisposed()) return; display.asyncExec(new Runnable() { public void run() { IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows(); if (windows == null) return; for (IWorkbenchWindow window : windows) { IDebugView view = (IDebugView)window.getActivePage().findView(IDebugUIConstants.ID_DEBUG_VIEW); if (view != null) ((StructuredViewer)view.getViewer()).refresh(launch); } } }); } } /** * Show error message box in active workbench window. * @param title - message box title. * @param error - error to be shown. */ public void showMessageBox(final String title, final Throwable error) { display.asyncExec(new Runnable() { public void run() { Shell shell = display.getActiveShell(); if (shell == null) { Shell[] shells = display.getShells(); HashSet<Shell> set = new HashSet<Shell>(); for (Shell s : shells) set.add(s); for (Shell s : shells) { if (s.getParent() != null) set.remove(s.getParent().getShell()); } for (Shell s : shells) shell = s; } MessageBox mb = new MessageBox(shell, SWT.ICON_ERROR | SWT.OK); mb.setText(title); mb.setMessage(getErrorMessage(error, true)); mb.open(); } }); } /** * Create human readable error message from a Throwable object. * @param error - a Throwable object. * @param multiline - true if multi-line text is allowed. * @return */ public static String getErrorMessage(Throwable error, boolean multiline) { StringBuffer buf = new StringBuffer(); while (error != null) { String msg = null; if (!multiline && error instanceof IErrorReport) { msg = Command.toErrorString(((IErrorReport)error).getAttributes()); } else { msg = error.getLocalizedMessage(); } if (msg == null || msg.length() == 0) msg = error.getClass().getName(); buf.append(msg); error = error.getCause(); if (error != null) { char ch = buf.charAt(buf.length() - 1); if (multiline && ch != '\n') { buf.append('\n'); } else if (ch != '.' && ch != ';') { buf.append(';'); } buf.append("Caused by:"); buf.append(multiline ? '\n' : ' '); } } if (buf.length() > 0) { char ch = buf.charAt(buf.length() - 1); if (multiline && ch != '\n') { buf.append('\n'); } } return buf.toString(); } /* * Open an editor for given editor input. * @param input - IEditorInput representing a source file to be shown in the editor * @param id - editor type ID * @param page - workbench page that will contain the editor * @return - IEditorPart if the editor was opened successfully, or null otherwise. */ private IEditorPart openEditor(final IEditorInput input, final String id, final IWorkbenchPage page) { final IEditorPart[] editor = new IEditorPart[]{ null }; Runnable r = new Runnable() { public void run() { if (!page.getWorkbenchWindow().getWorkbench().isClosing()) { try { editor[0] = page.openEditor(input, id, false, IWorkbenchPage.MATCH_ID|IWorkbenchPage.MATCH_INPUT); } catch (PartInitException e) { Activator.log("Cannot open editor", e); } } } }; BusyIndicator.showWhile(display, r); return editor[0]; } /* * Returns the line information for the given line in the given editor */ private IRegion getLineInformation(ITextEditor editor, int line) { IDocumentProvider provider = editor.getDocumentProvider(); IEditorInput input = editor.getEditorInput(); try { provider.connect(input); } catch (CoreException e) { return null; } try { IDocument document = provider.getDocument(input); if (document != null) return document.getLineInformation(line - 1); } catch (BadLocationException e) { } finally { provider.disconnect(input); } return null; } /** * Registers the given listener for suspend notifications. * @param listener suspend listener */ public synchronized void addSuspendTriggerListener(ISuspendTriggerListener listener) { suspend_trigger_listeners.add(listener); } /** * Unregisters the given listener for suspend notifications. * @param listener suspend listener */ public synchronized void removeSuspendTriggerListener(ISuspendTriggerListener listener) { suspend_trigger_listeners.remove(listener); } /* * Lazily run registered suspend listeners. * @param node - suspended context. */ private synchronized void runSuspendTrigger(final TCFNode node) { if (suspend_trigger_listeners.size() == 0) return; final ISuspendTriggerListener[] listeners = suspend_trigger_listeners.toArray( new ISuspendTriggerListener[suspend_trigger_listeners.size()]); final int generation = ++suspend_trigger_generation; if (wait_for_pc_update_after_step || wait_for_views_update_after_step) { launch.addPendingClient(suspend_trigger_listeners); } display.asyncExec(new Runnable() { public void run() { synchronized (TCFModel.this) { if (generation != suspend_trigger_generation) return; } for (final ISuspendTriggerListener listener : listeners) { try { listener.suspended(launch, node); } catch (Throwable x) { Activator.log(x); } } synchronized (TCFModel.this) { if (generation != suspend_trigger_generation) return; launch.removePendingClient(suspend_trigger_listeners); } } }); } /** * Set whether instruction stepping mode should be enabled or not. * @param enabled */ public void setInstructionSteppingEnabled(boolean enabled) { instruction_stepping_enabled = enabled; } /** * @return whether instruction stepping is enabled */ public boolean isInstructionSteppingEnabled() { return instruction_stepping_enabled; } }