diff options
author | eutarass | 2008-04-07 17:19:16 +0000 |
---|---|---|
committer | eutarass | 2008-04-07 17:19:16 +0000 |
commit | 3c90f4ac3612c74dd8309c85c6a96c77d860156c (patch) | |
tree | fcbd32366f0d90ea8abce04707feae360e62669f /plugins/org.eclipse.tm.tcf.debug.ui | |
parent | 74ecb17420fdbd286c06b324a8208c72175b0cf2 (diff) | |
download | org.eclipse.tcf-3c90f4ac3612c74dd8309c85c6a96c77d860156c.tar.gz org.eclipse.tcf-3c90f4ac3612c74dd8309c85c6a96c77d860156c.tar.xz org.eclipse.tcf-3c90f4ac3612c74dd8309c85c6a96c77d860156c.zip |
TCF debugger prototype: remote data caching logic is redesigned in order to improve performance and simplify the code.
Diffstat (limited to 'plugins/org.eclipse.tm.tcf.debug.ui')
17 files changed, 1219 insertions, 978 deletions
diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/ImageCache.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/ImageCache.java new file mode 100644 index 000000000..b97b06076 --- /dev/null +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/ImageCache.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2008 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; + +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.jface.resource.ImageDescriptor; +import org.osgi.framework.Bundle; + +public class ImageCache { + + private static final Map<String,ImageDescriptor> image_cache = new HashMap<String,ImageDescriptor>(); + + public static ImageDescriptor getImageDescriptor(String name) { + if (name == null) return null; + ImageDescriptor descriptor = image_cache.get(name); + if (descriptor == null) { + Bundle bundle = Platform.getBundle(Activator.PLUGIN_ID); + if (bundle != null){ + URL url = FileLocator.find(bundle, new Path(name), null); + if (url != null) descriptor = ImageDescriptor.createFromURL(url); + } + if (descriptor == null) { + bundle = Platform.getBundle("org.eclipse.debug.ui"); + if (bundle != null){ + URL url = FileLocator.find(bundle, new Path(name), null); + if (url != null) descriptor = ImageDescriptor.createFromURL(url); + } + } + if (descriptor == null) { + descriptor = ImageDescriptor.getMissingImageDescriptor(); + } + image_cache.put(name, descriptor); + } + return descriptor; + } +} diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/adapters/TCFLaunchAdapterFactory.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/adapters/TCFLaunchAdapterFactory.java index 616297dee..c68394991 100644 --- a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/adapters/TCFLaunchAdapterFactory.java +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/adapters/TCFLaunchAdapterFactory.java @@ -31,9 +31,14 @@ public class TCFLaunchAdapterFactory implements IAdapterFactory { ITerminateHandler.class }; + private final IElementLabelProvider launch_label_provider = new TCFLaunchLabelProvider(); + @SuppressWarnings("unchecked") public Object getAdapter(final Object from, final Class to) { if (from instanceof TCFLaunch) { + if (to.equals(IElementLabelProvider.class)) { + return launch_label_provider; + } final Object[] res = new Object[1]; Protocol.invokeAndWait(new Runnable() { public void run() { @@ -54,7 +59,6 @@ public class TCFLaunchAdapterFactory implements IAdapterFactory { }); if (res[0] != null) return res[0]; } - System.err.println(from.getClass().getName() + " -> " + to); return null; } diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/adapters/TCFLaunchLabelProvider.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/adapters/TCFLaunchLabelProvider.java new file mode 100644 index 000000000..2bf9f6a8d --- /dev/null +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/adapters/TCFLaunchLabelProvider.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2008 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.adapters; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.tm.internal.tcf.debug.model.TCFLaunch; +import org.eclipse.tm.internal.tcf.debug.ui.ImageCache; + +class TCFLaunchLabelProvider implements IElementLabelProvider { + + public void update(ILabelUpdate[] updates) { + for (int i = 0; i < updates.length; i++) { + ILabelUpdate result = updates[i]; + TCFLaunch launch = (TCFLaunch)result.getElement(); + result.setImageDescriptor(ImageCache.getImageDescriptor("icons/tcf.gif"), 0); + String status = ""; + if (launch.isConnecting()) status = "Connecting"; + else if (launch.isDisconnected()) status = "Disconnected"; + else if (launch.isTerminated()) status = "Terminated"; + Throwable error = launch.getError(); + if (error != null) { + status += " - " + error; + result.setForeground(new RGB(255, 0, 0), 0); + } + if (status.length() > 0) status = " (" + status + ")"; + String label = launch.getLaunchConfiguration().getName() + status; + result.setLabel(label, 0); + result.done(); + } + } +} diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFChildren.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFChildren.java index 1fb8e602d..12bae4898 100644 --- a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFChildren.java +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFChildren.java @@ -10,62 +10,94 @@ *******************************************************************************/ package org.eclipse.tm.internal.tcf.debug.ui.model; +import java.util.Collection; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; -public class TCFChildren { +import org.eclipse.tm.tcf.protocol.IChannel; +import org.eclipse.tm.tcf.protocol.IToken; - final TCFNode node; - final Map<String,TCFNode> children = new HashMap<String,TCFNode>(); +/** + * TCFChildren is a concrete type of TCF data cache that is used to cache a list of children. + */ +public abstract class TCFChildren extends TCFDataCache<Map<String,TCFNode>> { - protected boolean valid; + private final int pool_margin; + private final Map<String,TCFNode> node_pool = new LinkedHashMap<String,TCFNode>(32, 0.75f, true); + + TCFChildren(IChannel channel) { + super(channel); + pool_margin = 0; + } - TCFChildren(TCFNode node) { - this.node = node; + TCFChildren(IChannel channel, int pool_margin) { + super(channel); + this.pool_margin = pool_margin; } void dispose() { - TCFNode a[] = children.values().toArray(new TCFNode[children.size()]); + if (node_pool.isEmpty()) return; + TCFNode a[] = node_pool.values().toArray(new TCFNode[node_pool.size()]); for (int i = 0; i < a.length; i++) a[i].dispose(); - assert children.isEmpty(); + assert node_pool.isEmpty(); } void dispose(String id) { - children.remove(id); + node_pool.remove(id); + if (isValid()) getData().remove(id); + } + + private void flush(Map<String,TCFNode> data) { + node_pool.putAll(data); + if (node_pool.size() > data.size() + pool_margin) { + String[] arr = node_pool.keySet().toArray(new String[node_pool.size()]); + for (String id : arr) { + if (data.get(id) == null) { + node_pool.get(id).dispose(); + if (node_pool.size() <= data.size() + pool_margin) break; + } + } + } + } + + public void set(IToken token, Throwable error, Map<String,TCFNode> data) { + if (data != null) { + super.set(token, error, data); + flush(data); + } + else { + super.set(token, error, new HashMap<String,TCFNode>()); + } } - void doneValidate(Map<String,TCFNode> new_children) { - assert !node.disposed; - assert !valid; - valid = true; - if (children.size() > 0) { - TCFNode[] a = children.values().toArray(new TCFNode[children.size()]); - for (TCFNode n : a) if (new_children.get(n.id) != n) n.dispose(); + public void reset(Map<String,TCFNode> data) { + if (data != null) { + super.reset(data); + flush(data); } - for (TCFNode n : new_children.values()) { - assert n.parent == node; - children.put(n.id, n); + else { + super.reset(new HashMap<String,TCFNode>()); } - assert children.size() == new_children.size(); } - boolean validate() { - doneValidate(new HashMap<String,TCFNode>()); - return true; + void add(TCFNode n) { + node_pool.put(n.id, n); + if (isValid()) getData().put(n.id, n); } - void invalidate() { - assert !node.disposed; - TCFNode[] a = children.values().toArray(new TCFNode[children.size()]); - for (int i = 0; i < a.length; i++) a[i].invalidateNode(); - valid = false; + Collection<TCFNode> getNodes() { + return node_pool.values(); } int size() { - return children.size(); + assert isValid(); + return getData().size(); } TCFNode[] toArray() { - return children.values().toArray(new TCFNode[children.size()]); + assert isValid(); + Map<String,TCFNode> data = getData(); + return data.values().toArray(new TCFNode[data.size()]); } } diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFChildrenExecContext.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFChildrenExecContext.java index d31df810c..343edfb29 100644 --- a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFChildrenExecContext.java +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFChildrenExecContext.java @@ -11,7 +11,6 @@ package org.eclipse.tm.internal.tcf.debug.ui.model; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; @@ -29,8 +28,9 @@ import org.eclipse.tm.tcf.services.IRunControl; @SuppressWarnings("serial") public class TCFChildrenExecContext extends TCFChildren { - private Map<String,TCFNode> mem_children; - private Map<String,TCFNode> run_children; + private final TCFNode node; + private final TCFChildren mem_children; + private final TCFChildren run_children; // Track disposed IDs to detect violations of the communication protocol private LinkedHashMap<String,String> disposed_ids = new LinkedHashMap<String,String>() { @@ -39,162 +39,127 @@ public class TCFChildrenExecContext extends TCFChildren { } }; - TCFChildrenExecContext(TCFNode node) { - super(node); + TCFChildrenExecContext(final TCFNode node) { + super(node.model.getLaunch().getChannel()); + this.node = node; + mem_children = new TCFChildren(channel) { + @Override + protected boolean startDataRetrieval() { + IMemory mem = node.model.getLaunch().getService(IMemory.class); + if (mem == null) { + set(null, null, new HashMap<String,TCFNode>()); + return true; + } + assert command == null; + command = mem.getChildren(node.id, new IMemory.DoneGetChildren() { + public void doneGetChildren(IToken token, Exception error, String[] contexts) { + Map<String,TCFNode> data = null; + if (command == token && error == null) { + data = new HashMap<String,TCFNode>(); + for (String id : contexts) { + assert disposed_ids.get(id) == null; + TCFNode n = node.model.getNode(id); + if (n == null) n = new TCFNodeExecContext(node, id); + data.put(id, n); + } + } + set(token, error, data); + } + }); + return false; + } + }; + run_children = new TCFChildren(channel) { + @Override + protected boolean startDataRetrieval() { + IRunControl run = node.model.getLaunch().getService(IRunControl.class); + if (run == null) { + set(null, null, new HashMap<String,TCFNode>()); + return true; + } + assert command == null; + command = run.getChildren(node.id, new IRunControl.DoneGetChildren() { + public void doneGetChildren(IToken token, Exception error, String[] contexts) { + Map<String,TCFNode> data = null; + if (command == token && error == null) { + data = new HashMap<String,TCFNode>(); + for (String id : contexts) { + assert disposed_ids.get(id) == null; + TCFNode n = node.model.getNode(id); + if (n == null) n = new TCFNodeExecContext(node, id); + data.put(id, n); + } + } + set(token, error, data); + } + }); + return false; + } + }; } @Override - void dispose() { - HashSet<TCFNode> s = new HashSet<TCFNode>(); - s.addAll(children.values()); - if (mem_children != null) s.addAll(mem_children.values()); - if (run_children != null) s.addAll(run_children.values()); - for (TCFNode n : s) n.dispose(); - mem_children = null; - run_children = null; + protected void dispose() { + super.dispose(); + mem_children.dispose(); + run_children.dispose(); } @Override void dispose(String id) { super.dispose(id); - if (mem_children != null) mem_children.remove(id); - if (run_children != null) run_children.remove(id); + mem_children.dispose(id); + run_children.dispose(id); disposed_ids.put(id, id); } @Override - boolean validate() { - assert !node.disposed; - Map<String,TCFNode> new_children = new HashMap<String,TCFNode>(); - if (!validateMemoryChildren(new_children)) return false; - if (!validateRunControlChildren(new_children)) return false; - doneValidate(new_children); + protected boolean startDataRetrieval() { + TCFDataCache<?> pending = null; + if (!mem_children.validate()) pending = mem_children; + if (!run_children.validate()) pending = run_children; + if (pending != null) { + pending.wait(this); + return false; + } + Throwable error = null; + Map<String,TCFNode> data = new HashMap<String,TCFNode>(); + if (mem_children.getError() == null) data.putAll(mem_children.getData()); + else error = mem_children.getError(); + if (run_children.getError() == null) data.putAll(run_children.getData()); + else error = run_children.getError(); + set(null, error, data); return true; } - @Override - void invalidate() { - HashSet<TCFNode> s = new HashSet<TCFNode>(); - s.addAll(children.values()); - if (mem_children != null) s.addAll(mem_children.values()); - if (run_children != null) s.addAll(run_children.values()); - for (TCFNode n : s) n.invalidateNode(); - mem_children = null; - run_children = null; - valid = false; - } - void onContextAdded(IRunControl.RunControlContext context) { - assert !node.disposed; - if (run_children != null) { - String id = context.getID(); - TCFNodeExecContext n = (TCFNodeExecContext)node.model.getNode(id); - if (n == null) { - n = new TCFNodeExecContext(node, id); - n.setRunContext(context); - n.makeModelDelta(IModelDelta.INSERTED); - } - else { - n.setRunContext(context); - n.makeModelDelta(IModelDelta.STATE); - } - children.put(id, n); - run_children.put(id, n); - } - else { - node.invalidateNode(); + String id = context.getID(); + TCFNodeExecContext n = (TCFNodeExecContext)node.model.getNode(id); + if (n == null) { + n = new TCFNodeExecContext(node, id); node.makeModelDelta(IModelDelta.CONTENT); } + else { + n.makeModelDelta(IModelDelta.STATE); + } + add(n); + run_children.add(n); + n.setRunContext(context); } void onContextAdded(IMemory.MemoryContext context) { assert !node.disposed; - if (mem_children != null) { - String id = context.getID(); - TCFNodeExecContext n = (TCFNodeExecContext)node.model.getNode(id); - if (n == null) { - n = new TCFNodeExecContext(node, id); - n.setMemoryContext(context); - n.makeModelDelta(IModelDelta.INSERTED); - } - else { - n.setMemoryContext(context); - n.makeModelDelta(IModelDelta.STATE); - } - children.put(id, n); - mem_children.put(id, n); - } - else { - node.invalidateNode(); + String id = context.getID(); + TCFNodeExecContext n = (TCFNodeExecContext)node.model.getNode(id); + if (n == null) { + n = new TCFNodeExecContext(node, id); node.makeModelDelta(IModelDelta.CONTENT); } - } - - private boolean validateMemoryChildren(final Map<String,TCFNode> new_children) { - if (mem_children != null) { - new_children.putAll(mem_children); - return true; - } - IMemory mem = node.model.getLaunch().getService(IMemory.class); - if (mem == null) { - mem_children = new HashMap<String,TCFNode>(); - return true; + else { + n.makeModelDelta(IModelDelta.STATE); } - assert node.pending_command == null; - node.pending_command = mem.getChildren(node.id, new IMemory.DoneGetChildren() { - public void doneGetChildren(IToken token, Exception error, String[] contexts) { - if (node.pending_command != token) return; - node.pending_command = null; - mem_children = new_children; - mem_children.clear(); - if (error != null) { - node.node_error = error; - } - else { - for (String id : contexts) { - assert disposed_ids.get(id) == null; - TCFNode n = node.model.getNode(id); - if (n == null) n = new TCFNodeExecContext(node, id); - mem_children.put(id, n); - } - } - node.validateNode(); - } - }); - return false; - } - - private boolean validateRunControlChildren(final Map<String,TCFNode> new_children) { - if (run_children != null) { - new_children.putAll(run_children); - return true; - } - IRunControl run = node.model.getLaunch().getService(IRunControl.class); - if (run == null) { - run_children = new HashMap<String,TCFNode>(); - return true; - } - assert node.pending_command == null; - node.pending_command = run.getChildren(node.id, new IRunControl.DoneGetChildren() { - public void doneGetChildren(IToken token, Exception error, String[] contexts) { - if (node.pending_command != token) return; - node.pending_command = null; - run_children = new_children; - run_children.clear(); - if (error != null) { - node.node_error = error; - } - else { - for (String id : contexts) { - assert disposed_ids.get(id) == null; - TCFNode n = node.model.getNode(id); - if (n == null) n = new TCFNodeExecContext(node, id); - run_children.put(id, n); - } - } - node.validateNode(); - } - }); - return false; + add(n); + mem_children.add(n); + n.setMemoryContext(context); } } diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFChildrenRegisters.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFChildrenRegisters.java index 050bbfa7b..92998f559 100644 --- a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFChildrenRegisters.java +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFChildrenRegisters.java @@ -19,52 +19,44 @@ import org.eclipse.tm.tcf.services.IRegisters; public class TCFChildrenRegisters extends TCFChildren { - private boolean running; + private final TCFNode node; TCFChildrenRegisters(TCFNode node) { - super(node); + super(node.model.getLaunch().getChannel()); + this.node = node; } /** * Invalidate register values only, keep cached register attributes. */ void onSuspended() { - if (running || node.node_error != null) invalidate(); - for (TCFNode n : children.values()) ((TCFNodeRegister)n).onSuspended(); + for (TCFNode n : getNodes()) ((TCFNodeRegister)n).onSuspended(); + } + + void onRegistersChanged() { + for (TCFNode n : getNodes()) ((TCFNodeRegister)n).onRegistersChanged(); } @Override - boolean validate() { - assert !node.disposed; - assert !valid; - final Map<String,TCFNode> new_children = new HashMap<String,TCFNode>(); - running = !node.isSuspended(); - if (running) { - valid = true; - return true; - } + protected boolean startDataRetrieval() { IRegisters regs = node.model.getLaunch().getService(IRegisters.class); if (regs == null) { - doneValidate(new_children); + set(null, null, new HashMap<String,TCFNode>()); return true; } - assert node.pending_command == null; - node.pending_command = regs.getChildren(node.id, new IRegisters.DoneGetChildren() { + assert command == null; + command = regs.getChildren(node.id, new IRegisters.DoneGetChildren() { public void doneGetChildren(IToken token, Exception error, String[] contexts) { - if (node.pending_command != token) return; - node.pending_command = null; - if (error != null) { - node.node_error = error; - } - else { + Map<String,TCFNode> data = null; + if (command == token && error == null) { + data = new HashMap<String,TCFNode>(); for (String id : contexts) { TCFNode n = node.model.getNode(id); if (n == null) n = new TCFNodeRegister(node, id); - new_children.put(id, n); + data.put(id, n); } } - doneValidate(new_children); - node.validateNode(); + set(token, error, data); } }); return false; diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFChildrenStackTrace.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFChildrenStackTrace.java index ee77630e9..071641cfa 100644 --- a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFChildrenStackTrace.java +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFChildrenStackTrace.java @@ -11,7 +11,6 @@ package org.eclipse.tm.internal.tcf.debug.ui.model; import java.util.HashMap; -import java.util.Map; import org.eclipse.tm.tcf.protocol.IToken; import org.eclipse.tm.tcf.services.IStackTrace; @@ -19,95 +18,68 @@ import org.eclipse.tm.tcf.services.IStackTrace; public class TCFChildrenStackTrace extends TCFChildren { + private final TCFNodeExecContext node; private final TCFChildrenRegisters children_regs; - private final Map<String,TCFNodeStackFrame> frames_cache = - new HashMap<String,TCFNodeStackFrame>(); - - TCFChildrenStackTrace(TCFNode node, TCFChildrenRegisters children_regs) { - super(node); + TCFChildrenStackTrace(TCFNodeExecContext node, TCFChildrenRegisters children_regs) { + super(node.model.getLaunch().getChannel(), 16); + this.node = node; this.children_regs = children_regs; } - @Override - void dispose() { - TCFNode arr[] = frames_cache.values().toArray(new TCFNode[frames_cache.size()]); - for (int i = 0; i < arr.length; i++) arr[i].dispose(); - assert frames_cache.isEmpty(); - assert children.isEmpty(); - } - - @Override - void dispose(String id) { - super.dispose(id); - frames_cache.remove(id); - } - void onSourceMappingChange() { - for (TCFNodeStackFrame n : frames_cache.values()) n.onSourceMappingChange(); + for (TCFNode n : getNodes()) ((TCFNodeStackFrame)n).onSourceMappingChange(); } void onSuspended() { - for (TCFNodeStackFrame n : frames_cache.values()) n.onSuspended(); - valid = false; + for (TCFNode n : getNodes()) ((TCFNodeStackFrame)n).onSuspended(); + reset(); + } + + void onRegistersChanged() { + for (TCFNode n : getNodes()) ((TCFNodeStackFrame)n).onRegistersChanged(); } void onResumed() { - valid = false; + reset(null); } @Override - boolean validate() { - final Map<String,TCFNode> new_children = new HashMap<String,TCFNode>(); + protected boolean startDataRetrieval() { + final HashMap<String,TCFNode> data = new HashMap<String,TCFNode>(); if (!node.isSuspended()) { - doneValidate(new_children); + set(null, null, data); return true; } String nm = node.id + "-TF"; - TCFNodeStackFrame n = frames_cache.get(nm); - if (n == null) n = (TCFNodeStackFrame)node.model.getNode(nm); + TCFNodeStackFrame n = (TCFNodeStackFrame)node.model.getNode(nm); if (n == null) n = new TCFNodeStackFrame(node, nm, children_regs); - new_children.put(n.id, n); - frames_cache.put(n.id, n); + data.put(n.id, n); IStackTrace st = node.model.getLaunch().getService(IStackTrace.class); if (st == null) { - doneValidate(new_children); + set(null, null, data); return true; } - assert node.pending_command == null; - node.pending_command = st.getChildren(node.id, new IStackTrace.DoneGetChildren() { + assert command == null; + command = st.getChildren(node.id, new IStackTrace.DoneGetChildren() { public void doneGetChildren(IToken token, Exception error, String[] contexts) { - if (node.pending_command != token) return; - node.pending_command = null; - if (error != null) { - node.node_error = error; - } - else { + if (command == token && error == null) { int cnt = contexts.length; for (String id : contexts) { - TCFNodeStackFrame n = frames_cache.get(id); - if (n == null) n = (TCFNodeStackFrame)node.model.getNode(id); + TCFNodeStackFrame n = (TCFNodeStackFrame)node.model.getNode(id); if (n == null || n.getFrameNo() != cnt) { if (n != null) n.dispose(); n = new TCFNodeStackFrame(node, id, cnt); } - assert n.getFrameNo() == cnt; + assert n.getFrameNo() != 0; assert n.id.equals(id); assert n.parent == node; - new_children.put(id, n); - frames_cache.put(id, n); + n.setFrameNo(cnt); + data.put(id, n); cnt--; } - if (frames_cache.size() > new_children.size() + 32) { - // Trim frame cache - TCFNode arr[] = frames_cache.values().toArray(new TCFNode[frames_cache.size()]); - for (int i = 0; i < arr.length; i++) { - if (new_children.get(arr[i].id) == null) arr[i].dispose(); - } - } } - doneValidate(new_children); - node.validateNode(); + set(token, error, data); } }); return false; diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFDataCache.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFDataCache.java new file mode 100644 index 000000000..5584a02ed --- /dev/null +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFDataCache.java @@ -0,0 +1,178 @@ +/******************************************************************************* + * Copyright (c) 2008 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.HashSet; + +import org.eclipse.tm.tcf.protocol.IChannel; +import org.eclipse.tm.tcf.protocol.IToken; +import org.eclipse.tm.tcf.protocol.Protocol; + +/** + * Objects of this class are used cache TCF remote data. + * The cache is asynchronous state machine. The states are: + * 1. Valid - cache is in sync with remote data, use getError() and getData() to get cached data; + * 2. Invalid - cache is out out of sync, start data retrieval by calling validate(); + * 3. Pending - cache is waiting result of a command that was sent to remote peer. + * @param <V> - type of data to be stored in the cache. + */ +public abstract class TCFDataCache<V> implements Runnable { + + private Throwable error; + private boolean valid; + private V data; + + protected final IChannel channel; + protected IToken command; + + private final HashSet<Runnable> waiting_list = new HashSet<Runnable>(); + + public TCFDataCache(IChannel channel) { + assert channel != null; + this.channel = channel; + } + + /** + * @return true if cache contains up-to-date data (or data error). + */ + public boolean isValid() { + return valid; + } + + /** + * @return true if data retrieval is in progress. + */ + public boolean isPending() { + return command != null; + } + + /** + * @return error object if data retrieval ended with an error, or null if retrieval was successful. + * Note: It is prohibited to call this method when cache is not valid. + */ + public Throwable getError() { + assert valid; + return error; + } + + /** + * @return cached data object. + * Note: It is prohibited to call this method when cache is not valid. + */ + public V getData() { + assert valid; + return data; + } + + /** + * Notify waiting clients about cache state change and remove then from wait list. + * It is responsibility of clients to check if state change was one they are waiting for. + */ + public void run() { + if (waiting_list.isEmpty()) return; + Runnable[] arr = waiting_list.toArray(new Runnable[waiting_list.size()]); + waiting_list.clear(); + for (Runnable r : arr) r.run(); + } + + /** + * Add a client call-back to cache wait list. + * @param req + */ + public void wait(Runnable cb) { + assert !valid; + if (cb != null) waiting_list.add(cb); + } + + /** + * Initiate data retrieval if the cache is not valid. + * @return + */ + public boolean validate() { + assert Protocol.isDispatchThread(); + if (channel.getState() != IChannel.STATE_OPEN) { + error = null; + command = null; + valid = true; + data = null; + } + else { + if (command != null) return false; + if (!valid && !startDataRetrieval()) return false; + } + assert valid; + assert command == null; + run(); + return true; + } + + /** + * End cache pending state. + * @param token - pending command handle. + * @param error - data retrieval error or null + * @param data - up-to-date data object + */ + public void set(IToken token, Throwable error, V data) { + assert Protocol.isDispatchThread(); + if (command != token) return; + command = null; + if (channel.getState() != IChannel.STATE_OPEN) data = null; + this.error = error; + this.data = data; + valid = true; + run(); + } + + /** + * Force cache to become valid, cancel pending data retrieval if any. + * @param data - up-to-date data object + */ + public void reset(V data) { + if (command != null) { + command.cancel(); + command = null; + } + this.data = data; + error = null; + valid = true; + run(); + } + + /** + * Invalidate the cache. If retrieval is in progress - let it continue. + */ + public void reset() { + error = null; + valid = false; + data = null; + run(); + } + + /** + * Force cache to invalid state, cancel pending data retrieval if any. + */ + public void cancel() { + if (command != null) { + command.cancel(); + command = null; + } + error = null; + valid = false; + data = null; + run(); + } + + /** + * Sub-classes should override this method to implement actual data retrieval logic. + * @return true is all done, false if retrieval is in progress. + */ + protected abstract boolean startDataRetrieval(); +} diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFModel.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFModel.java index cddebcf9a..8ebb9c1d9 100644 --- a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFModel.java +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFModel.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.tm.internal.tcf.debug.ui.model; +import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.TreeSet; @@ -33,7 +34,6 @@ 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.IPresentationContext; -import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.swt.widgets.Display; import org.eclipse.tm.internal.tcf.debug.model.TCFLaunch; @@ -55,15 +55,14 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, private final Display display; private final TCFLaunch launch; - private final TCFNodeLaunch launch_node; private final Map<IPresentationContext,TCFModelProxy> model_proxies = new HashMap<IPresentationContext,TCFModelProxy>(); private final Map<String,TCFNode> id2node = new HashMap<String,TCFNode>(); - private final Map<TCFNode,ModelDelta> deltas = new HashMap<TCFNode,ModelDelta>(); @SuppressWarnings("unchecked") private final Map<Class,Object> commands = new HashMap<Class,Object>(); private final TreeSet<FutureTask> queue = new TreeSet<FutureTask>(); + private TCFNodeLaunch launch_node; private boolean disposed; private int future_task_cnt; @@ -276,7 +275,6 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, TCFModel(Display display, TCFLaunch launch) { this.display = display; this.launch = launch; - launch_node = new TCFNodeLaunch(TCFModel.this); commands.put(ISuspendHandler.class, new SuspendCommand(this)); commands.put(IResumeHandler.class, new ResumeCommand(this)); commands.put(ITerminateHandler.class, new TerminateCommand(this)); @@ -297,13 +295,13 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, void onConnected() { assert Protocol.isDispatchThread(); + 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); IRegisters reg = launch.getService(IRegisters.class); if (reg != null) reg.addListener(reg_listener); - launch_node.invalidateNode(); launch_node.makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); fireModelChanged(); } @@ -334,9 +332,15 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, }); } + Collection<TCFModelProxy> getModelProxyList() { + return model_proxies.values(); + } + void launchChanged() { - launch_node.makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); - fireModelChanged(); + if (launch_node != null) { + launch_node.makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); + fireModelChanged(); + } } void dispose() { @@ -365,25 +369,10 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, assert Protocol.isDispatchThread(); id2node.remove(id); } - - ModelDelta getDelta(TCFNode node) { - return deltas.get(node); - } - - void addDelta(TCFNode node, ModelDelta delta) { - assert deltas.get(node) == null; - deltas.put(node, delta); - } - + void fireModelChanged() { assert Protocol.isDispatchThread(); - ModelDelta delta = deltas.get(launch_node); - assert (delta == null) == deltas.isEmpty(); - if (delta != null) { - deltas.clear(); - IModelDelta top = delta.getParentDelta(); - for (TCFModelProxy p : model_proxies.values()) p.fireModelChanged(top); - } + for (TCFModelProxy p : model_proxies.values()) p.fireModelChanged(); } public Display getDisplay() { @@ -415,33 +404,61 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, public void update(IChildrenCountUpdate[] updates) { for (int i = 0; i < updates.length; i++) { Object o = updates[i].getElement(); - if (o instanceof TCFLaunch) launch_node.update(updates[i]); - else ((TCFNode)o).update(updates[i]); + if (o instanceof TCFLaunch) { + if (launch_node != null) { + launch_node.update(updates[i]); + } + else { + updates[i].setChildCount(0); + updates[i].done(); + } + } + else { + ((TCFNode)o).update(updates[i]); + } } } public void update(IChildrenUpdate[] updates) { for (int i = 0; i < updates.length; i++) { Object o = updates[i].getElement(); - if (o instanceof TCFLaunch) launch_node.update(updates[i]); - else ((TCFNode)o).update(updates[i]); + if (o instanceof TCFLaunch) { + if (launch_node != null) { + launch_node.update(updates[i]); + } + else { + updates[i].done(); + } + } + else { + ((TCFNode)o).update(updates[i]); + } } } public void update(IHasChildrenUpdate[] updates) { for (int i = 0; i < updates.length; i++) { Object o = updates[i].getElement(); - if (o instanceof TCFLaunch) launch_node.update(updates[i]); - else ((TCFNode)o).update(updates[i]); + if (o instanceof TCFLaunch) { + if (launch_node != null) { + launch_node.update(updates[i]); + } + else { + updates[i].setHasChilren(false); + updates[i].done(); + } + } + else { + ((TCFNode)o).update(updates[i]); + } } } public void update(ILabelUpdate[] updates) { for (int i = 0; i < updates.length; i++) { Object o = updates[i].getElement(); - assert o != launch_node; - if (o instanceof TCFLaunch) launch_node.update(updates[i]); - else ((TCFNode)o).update(updates[i]); + assert !(o instanceof TCFLaunch); + ((TCFNode)o).update(updates[i]); } } diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFModelManager.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFModelManager.java index b979d7989..4b5f0efee 100644 --- a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFModelManager.java +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFModelManager.java @@ -19,6 +19,7 @@ import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchesListener; import org.eclipse.swt.widgets.Display; import org.eclipse.tm.internal.tcf.debug.model.TCFLaunch; +import org.eclipse.tm.tcf.protocol.IChannel; import org.eclipse.tm.tcf.protocol.Protocol; @@ -102,7 +103,10 @@ public class TCFModelManager { if (model == null) { model = new TCFModel(display, launch); models.put(launch, model); - if (launch.getChannel() != null) tcf_launch_listener.onConnected(launch); + IChannel channel = launch.getChannel(); + if (channel != null && channel.getState() == IChannel.STATE_OPEN) { + tcf_launch_listener.onConnected(launch); + } } return model; } diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFModelProxy.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFModelProxy.java index 6eb014de3..ac5694bc1 100644 --- a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFModelProxy.java +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFModelProxy.java @@ -10,13 +10,21 @@ *******************************************************************************/ package org.eclipse.tm.internal.tcf.debug.ui.model; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.internal.ui.viewers.provisional.AbstractModelProxy; +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.ModelDelta; import org.eclipse.jface.viewers.Viewer; +import org.eclipse.tm.tcf.protocol.Protocol; public class TCFModelProxy extends AbstractModelProxy implements IModelProxy { private final TCFModel model; + private final Map<TCFNode,ModelDelta> deltas = new HashMap<TCFNode,ModelDelta>(); TCFModelProxy(TCFModel model) { this.model = model; @@ -31,4 +39,27 @@ public class TCFModelProxy extends AbstractModelProxy implements IModelProxy { model.onProxyDisposed(this); super.dispose(); } + + ModelDelta getDelta(TCFNode node) { + return deltas.get(node); + } + + void addDelta(TCFNode node, ModelDelta delta) { + assert deltas.get(node) == null; + assert delta.getElement() == node || delta.getElement() == model.getLaunch() && node == model.getRootNode(); + deltas.put(node, delta); + } + + void fireModelChanged() { + assert Protocol.isDispatchThread(); + ModelDelta delta = deltas.get(model.getRootNode()); + assert (delta == null) == deltas.isEmpty(); + if (delta != null) { + deltas.clear(); + assert delta.getElement() == model.getLaunch(); + IModelDelta top = delta.getParentDelta(); + assert top.getElement() == DebugPlugin.getDefault().getLaunchManager(); + fireModelChanged(top); + } + } } diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFNode.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFNode.java index f7d4d86e9..861e91057 100644 --- a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFNode.java +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFNode.java @@ -10,22 +10,11 @@ *******************************************************************************/ package org.eclipse.tm.internal.tcf.debug.ui.model; -import java.net.URL; -import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import org.eclipse.core.runtime.FileLocator; -import org.eclipse.core.runtime.Path; -import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.PlatformObject; import org.eclipse.core.runtime.Status; -import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.ILaunch; -import org.eclipse.debug.core.model.IMemoryBlock; -import org.eclipse.debug.core.model.IMemoryBlockExtension; -import org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension; 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.IColumnPresentationFactory; @@ -35,21 +24,19 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdat 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.IModelProxyFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.swt.graphics.RGB; -import org.eclipse.tm.tcf.protocol.IToken; +import org.eclipse.tm.internal.tcf.debug.ui.ImageCache; +import org.eclipse.tm.tcf.protocol.IChannel; import org.eclipse.tm.tcf.protocol.Protocol; import org.eclipse.tm.tcf.services.IMemory; import org.eclipse.tm.tcf.services.IRunControl; -import org.osgi.framework.Bundle; /** * TCFNode is base class for all TCF debug model elements. */ -public abstract class TCFNode extends PlatformObject -implements IMemoryBlockRetrievalExtension, Comparable<TCFNode> { +public abstract class TCFNode extends PlatformObject implements Comparable<TCFNode> { protected final String id; protected final TCFNode parent; @@ -152,6 +139,10 @@ implements IMemoryBlockRetrievalExtension, Comparable<TCFNode> { public String getAddress() { return null; } + + boolean isNodeContentVisibleInContext(IPresentationContext p) { + return true; + } /** * Retrieve children count for a presentation context. @@ -160,7 +151,8 @@ implements IMemoryBlockRetrievalExtension, Comparable<TCFNode> { final void update(final IChildrenCountUpdate result) { new TCFRunnable(model.getDisplay(), result) { public void run() { - if (!disposed && model.getLaunch().getChannel() != null) { + IChannel channel = model.getLaunch().getChannel(); + if (!disposed && channel.getState() == IChannel.STATE_OPEN) { if (!validateNode(this)) return; getData(result); } @@ -180,7 +172,8 @@ implements IMemoryBlockRetrievalExtension, Comparable<TCFNode> { final void update(final IChildrenUpdate result) { new TCFRunnable(model.getDisplay(), result) { public void run() { - if (!disposed && model.getLaunch().getChannel() != null) { + IChannel channel = model.getLaunch().getChannel(); + if (!disposed && channel.getState() == IChannel.STATE_OPEN) { if (!validateNode(this)) return; getData(result); } @@ -197,7 +190,8 @@ implements IMemoryBlockRetrievalExtension, Comparable<TCFNode> { final void update(final IHasChildrenUpdate result) { new TCFRunnable(model.getDisplay(), result) { public void run() { - if (!disposed && model.getLaunch().getChannel() != null) { + IChannel channel = model.getLaunch().getChannel(); + if (!disposed && channel.getState() == IChannel.STATE_OPEN) { if (!validateNode(this)) return; getData(result); } @@ -217,16 +211,10 @@ implements IMemoryBlockRetrievalExtension, Comparable<TCFNode> { final void update(final ILabelUpdate result) { new TCFRunnable(model.getDisplay(), result) { public void run() { - if (!disposed) { + IChannel channel = model.getLaunch().getChannel(); + if (!disposed && channel.getState() == IChannel.STATE_OPEN) { if (!validateNode(this)) return; - if (node_error != null) { - result.setForeground(new RGB(255, 0, 0), 0); - result.setLabel(node_error.getClass().getName() + - ": " + node_error.getMessage(), 0); - } - else { - getData(result); - } + getData(result); } else { result.setLabel("...", 0); @@ -277,23 +265,43 @@ implements IMemoryBlockRetrievalExtension, Comparable<TCFNode> { * @param result - label update request. */ protected void getData(ILabelUpdate result) { - result.setImageDescriptor(getImageDescriptor(getImageName()), 0); + result.setImageDescriptor(ImageCache.getImageDescriptor(getImageName()), 0); result.setLabel(id, 0); } /** - * Create ModelDelta for changes in this node. + * Create and post ModelDelta for changes in this node. + * @param flags - description of what has changed: IModelDelta.ADDED, IModelDelta.REMOVED, etc. + */ + final void makeModelDelta(int flags) { + for (TCFModelProxy p : model.getModelProxyList()) { + int f = flags & getRelevantModelDeltaFlags(p.getPresentationContext()); + if (f != 0) makeModelDelta(p, f); + } + } + + /** + * Return bit set of model delta flags relevant for this node in given presentation context. + * Sub-classes are supposed to override this method. + * @param p - presentation context + * @return bit set of model delta flags + */ + int getRelevantModelDeltaFlags(IPresentationContext p) { + return IModelDelta.CONTENT | IModelDelta.STATE; + } + + /** + * Create and post ModelDelta for changes in this node, relevant for given presentation context. + * @param p - target presentation context. * @param flags - description of what has changed: IModelDelta.ADDED, IModelDelta.REMOVED, etc. * @return - ModelDelta that describes node changes. */ - ModelDelta makeModelDelta(int flags) { - int count = -1; - int index = -1; - ModelDelta delta = model.getDelta(this); - if (delta == null || delta.getChildCount() != count || delta.getIndex() != index) { - ModelDelta parent_delta = parent.makeModelDelta(IModelDelta.NO_CHANGE); - delta = parent_delta.addNode(this, index, flags, count); - model.addDelta(this, delta); + ModelDelta makeModelDelta(TCFModelProxy p, int flags) { + ModelDelta delta = p.getDelta(this); + if (delta == null) { + ModelDelta parent_delta = parent.makeModelDelta(p, IModelDelta.NO_CHANGE); + delta = parent_delta.addNode(this, flags); + p.addDelta(this, delta); } else { delta.setFlags(delta.getFlags() | flags); @@ -303,185 +311,50 @@ implements IMemoryBlockRetrievalExtension, Comparable<TCFNode> { /*--------------------------------------------------------------------------------------*/ /* Node data retrieval state machine */ - - protected Throwable node_error; - protected IToken pending_command; - private final Collection<TCFRunnable> wait_list = new ArrayList<TCFRunnable>(); /** * Invalidate the node - flush all cached data. * Subclasses should override this method to flush any additional data. - * Subclasses should call super.invalidateNode(). - */ - public void invalidateNode() { - // cancel current data retrieval command - if (pending_command != null) { - pending_command.cancel(); - pending_command = null; - } - - // cancel waiting monitors - if (!wait_list.isEmpty()) { - for (TCFRunnable r : wait_list) r.cancel(); - wait_list.clear(); - } - - node_error = null; - } - - /** - * Validate node - retrieve and put into a cache missing data from remote peer. - * Validation is done asynchronously. If the node is already valid, - * the method should return true. Otherwise, it returns false, - * and later, when the node becomes valid, call-backs from 'wait_list' are invoked. - * @return true if the node is already valid, false if validation is started. */ - public final boolean validateNode() { - assert Protocol.isDispatchThread(); - assert !disposed; - if (pending_command != null) { - return false; - } - else if (model.getLaunch().getChannel() == null) { - node_error = null; - } - else if (node_error == null && !validateNodeData()) { - return false; - } - if (!wait_list.isEmpty()) { - Runnable[] arr = wait_list.toArray(new Runnable[wait_list.size()]); - wait_list.clear(); - for (Runnable r : arr) r.run(); - } - return true; - } + public abstract void invalidateNode(); /** * Validate node - retrieve and put into a cache missing data from remote peer. + * The method should initiate retrieval of all data needed by TCFNode.update() methods. * Validation is done asynchronously. If the node is already valid, * the method should return true. Otherwise, it returns false, - * adds 'done' into 'wait_list', and later, when the node becomes valid, - * call-backs from 'wait_list' are invoked. - * @param done - call-back object to call when node becomes valid. + * adds 'done' into 'wait_list', and later call-backs from 'wait_list' are invoked. + * Note: activation of call-back does not mean all data is retrieved, + * it only means that node state changed, client should call validateNode() again, + * until the method returns true. + * @param done - call-back object to call when node state changes. * @return true if the node is already valid, false if validation is started. */ - public final boolean validateNode(TCFRunnable done) { - assert done != null; - if (!validateNode()) { - wait_list.add(done); - return false; - } - return true; - } + public abstract boolean validateNode(Runnable done); - private class ValidateNodes extends TCFRunnable { - - int cnt = 0; - private IToken command; - - ValidateNodes(Collection<TCFNode> nodes) { - for (TCFNode n : nodes) { - if (!n.validateNode(this)) cnt++; - } - if (cnt > 0) { - pending_command = command = new IToken() { - public boolean cancel() { - return false; - } - }; - } - } - - public void run() { - cnt--; - assert cnt >= 0; - if (cnt != 0) return; - if (command != pending_command) return; - Protocol.invokeLater(new Runnable() { - public void run() { - if (command != pending_command) return; - pending_command = null; - validateNode(); - } - }); - } - - @Override - public void cancel() { - run(); - } - } - /** * Subclasses can use this method to validate a collection of nodes. * Validation of multiple nodes is expensive and should be avoided * when possible. * - * Validation is performed in background, and call-backs from 'wait_list' are - * activated when validation is done. + * Validation is performed in background, and 'done' call-back is + * activated when nodes state changes. * * @param nodes * @return true if all nodes are already valid, false if validation is started. */ - protected boolean validateNodes(Collection<TCFNode> nodes) { - if (nodes.isEmpty()) return true; - if (pending_command != null) return false; - return new ValidateNodes(nodes).cnt == 0; - } - - /** - * Subclasses should override this method to implement data retrieval that - * is specific for this node. - * - * Data retrieval should be performed in background, and it should call - * validateNode() when retrieval is done. - * - * @return true if the node is already valid, false if data retrieval is started. - */ - protected abstract boolean validateNodeData(); - - /*--------------------------------------------------------------------------------------*/ - /* Memory Block Retrieval */ - - public IMemoryBlockExtension getExtendedMemoryBlock(String addr, Object ctx) throws DebugException { - assert ctx == this; - return getMemoryBlock(addr, 0); - } - - public IMemoryBlock getMemoryBlock(long addr, long length) throws DebugException { - return getMemoryBlock(Long.toString(addr), length); - } - - public boolean supportsStorageRetrieval() { - return getMemoryContext() != null; - } - - private IMemoryBlockExtension getMemoryBlock(String addr, long length) throws DebugException { - assert !Protocol.isDispatchThread(); - // TODO: MemoryBlock - return null; + protected boolean validateNodes(Collection<TCFNode> nodes, Runnable done) { + TCFNode pending = null; + for (TCFNode n : nodes) { + if (!n.validateNode(null)) pending = n; + } + if (pending != null && !pending.validateNode(done)) return false; + return true; } - + /*--------------------------------------------------------------------------------------*/ /* Misc */ - private static final Map<String,ImageDescriptor> image_cache = new HashMap<String,ImageDescriptor>(); - - static ImageDescriptor getImageDescriptor(String name) { - if (name == null) return null; - ImageDescriptor descriptor = image_cache.get(name); - if (descriptor == null) { - descriptor = ImageDescriptor.getMissingImageDescriptor(); - Bundle bundle = Platform.getBundle("org.eclipse.debug.ui"); - if (bundle != null){ - URL url = FileLocator.find(bundle, new Path(name), null); - descriptor = ImageDescriptor.createFromURL(url); - } - image_cache.put(name, descriptor); - } - return descriptor; - } - protected String getImageName() { return null; } diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFNodeExecContext.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFNodeExecContext.java index db2f09164..3e27e700b 100644 --- a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFNodeExecContext.java +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFNodeExecContext.java @@ -10,7 +10,9 @@ *******************************************************************************/ package org.eclipse.tm.internal.tcf.debug.ui.model; +import java.math.BigInteger; import java.util.Arrays; +import java.util.LinkedHashMap; import java.util.Map; import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; @@ -18,44 +20,109 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; 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.ModelDelta; import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.tm.internal.tcf.debug.ui.ImageCache; +import org.eclipse.tm.tcf.protocol.IChannel; import org.eclipse.tm.tcf.protocol.IToken; import org.eclipse.tm.tcf.protocol.Protocol; import org.eclipse.tm.tcf.services.IMemory; import org.eclipse.tm.tcf.services.IRunControl; +@SuppressWarnings("serial") public class TCFNodeExecContext extends TCFNode { private final TCFChildrenExecContext children_exec; private final TCFChildrenStackTrace children_stack; private final TCFChildrenRegisters children_regs; - private IMemory.MemoryContext mem_context; - private IRunControl.RunControlContext run_context; + private final TCFDataCache<IMemory.MemoryContext> mem_context; + private final TCFDataCache<IRunControl.RunControlContext> run_context; + private final TCFDataCache<ContextState> state; - private boolean suspended; - private String suspended_pc; - private String suspended_reason; - @SuppressWarnings("unused") - private Map<String,Object> suspended_params; - private boolean running; - private boolean terminated; - @SuppressWarnings("unused") - private String exception_msg; + private final Map<BigInteger,TCFSourceRef> line_info_cache; - private boolean valid_mem_ctx; - private boolean valid_run_ctx; - private boolean valid_state; + private static class ContextState { + boolean suspended; + String suspended_pc; + String suspended_reason; + boolean terminated; + } private int resumed_cnt; - TCFNodeExecContext(TCFNode parent, String id) { + TCFNodeExecContext(TCFNode parent, final String id) { super(parent, id); children_exec = new TCFChildrenExecContext(this); children_regs = new TCFChildrenRegisters(this); children_stack = new TCFChildrenStackTrace(this, children_regs); + line_info_cache = new LinkedHashMap<BigInteger,TCFSourceRef>() { + @SuppressWarnings("unchecked") + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > 256; + } + }; + IChannel channel = model.getLaunch().getChannel(); + mem_context = new TCFDataCache<IMemory.MemoryContext>(channel) { + @Override + protected boolean startDataRetrieval() { + assert command == null; + IMemory mem = model.getLaunch().getService(IMemory.class); + if (mem == null) { + set(null, null, null); + return true; + } + command = mem.getContext(id, new IMemory.DoneGetContext() { + public void doneGetContext(IToken token, Exception error, IMemory.MemoryContext context) { + set(token, error, context); + } + }); + return false; + } + }; + run_context = new TCFDataCache<IRunControl.RunControlContext>(channel) { + @Override + protected boolean startDataRetrieval() { + assert command == null; + IRunControl run = model.getLaunch().getService(IRunControl.class); + if (run == null) { + set(null, null, null); + return true; + } + command = run.getContext(id, new IRunControl.DoneGetContext() { + public void doneGetContext(IToken token, Exception error, IRunControl.RunControlContext context) { + set(token, error, context); + } + }); + return false; + } + }; + state = new TCFDataCache<ContextState>(channel) { + @Override + protected boolean startDataRetrieval() { + assert command == null; + if (!run_context.validate()) { + run_context.wait(this); + return false; + } + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx == null || !ctx.hasState()) { + set(null, null, null); + return true; + } + command = ctx.getState(new IRunControl.DoneGetState() { + public void doneGetState(IToken token, Exception error, boolean suspended, String pc, String reason, Map<String,Object> params) { + ContextState s = new ContextState(); + s.suspended = suspended; + s.suspended_pc = pc; + s.suspended_reason = reason; + set(token, error, s); + } + }); + return false; + } + }; } @Override @@ -73,49 +140,70 @@ public class TCFNodeExecContext extends TCFNode { children_regs.dispose(id); } - void setRunContext(IRunControl.RunControlContext ctx) { - run_context = ctx; - valid_run_ctx = true; + void setRunContext(IRunControl.RunControlContext ctx) { + run_context.reset(ctx); } void setMemoryContext(IMemory.MemoryContext ctx) { - mem_context = ctx; - valid_mem_ctx = true; + mem_context.reset(ctx); + } + + Map<BigInteger,TCFSourceRef> getLineInfoCache() { + return line_info_cache; } @Override public IRunControl.RunControlContext getRunContext() { assert Protocol.isDispatchThread(); - return run_context; + if (!run_context.isValid()) return null; + return run_context.getData(); } @Override public IMemory.MemoryContext getMemoryContext() { assert Protocol.isDispatchThread(); - return mem_context; + if (!mem_context.isValid()) return null; + return mem_context.getData(); } @Override public boolean isRunning() { assert Protocol.isDispatchThread(); - return running; + if (!run_context.isValid()) return false; + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx == null || !ctx.hasState()) return false; + if (!state.isValid()) return false; + ContextState s = state.getData(); + return s != null && !s.suspended; } @Override public boolean isSuspended() { assert Protocol.isDispatchThread(); - return suspended; + if (!run_context.isValid()) return false; + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx == null || !ctx.hasState()) return false; + if (!state.isValid()) return false; + ContextState s = state.getData(); + return s != null && s.suspended; } @Override public String getAddress() { assert Protocol.isDispatchThread(); - return suspended_pc; + if (!run_context.isValid()) return null; + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx == null || !ctx.hasState()) return null; + if (!state.isValid()) return null; + ContextState s = state.getData(); + if (s == null) return null; + return s.suspended_pc; } @Override protected void getData(IChildrenCountUpdate result) { - if (run_context != null && run_context.hasState()) { + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null && ctx.hasState()) { if (IDebugUIConstants.ID_REGISTER_VIEW.equals(result.getPresentationContext().getId())) { result.setChildCount(children_regs.size()); } @@ -132,7 +220,8 @@ public class TCFNodeExecContext extends TCFNode { protected void getData(IChildrenUpdate result) { int offset = 0; TCFNode[] arr = null; - if (run_context != null && run_context.hasState()) { + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null && ctx.hasState()) { if (IDebugUIConstants.ID_REGISTER_VIEW.equals(result.getPresentationContext().getId())) { arr = children_regs.toArray(); } @@ -154,7 +243,8 @@ public class TCFNodeExecContext extends TCFNode { @Override protected void getData(IHasChildrenUpdate result) { - if (run_context != null && run_context.hasState()) { + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null && ctx.hasState()) { if (IDebugUIConstants.ID_REGISTER_VIEW.equals(result.getPresentationContext().getId())) { result.setHasChilren(children_regs.size() > 0); } @@ -169,43 +259,42 @@ public class TCFNodeExecContext extends TCFNode { @Override protected void getData(ILabelUpdate result) { - result.setImageDescriptor(getImageDescriptor(getImageName()), 0); + result.setImageDescriptor(ImageCache.getImageDescriptor(getImageName()), 0); String label = id; - if (run_context != null) { - if (run_context.hasState()) { - if (running) { + Throwable error = run_context.getError(); + if (error != null) { + result.setForeground(new RGB(255, 0, 0), 0); + label += ": " + error.getClass().getName() + ": " + error.getMessage(); + } + else { + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null) { + if (isRunning()) { label += " (Running)"; } - else if (suspended) { - if (suspended_reason != null) { - label += " (" + suspended_reason + ")"; + else if (isSuspended()) { + String r = state.getData().suspended_reason; + if (r != null) { + label += " (" + r + ")"; } else { label += " (Suspended)"; } } + String file = (String)ctx.getProperties().get("File"); + if (file != null) label += " " + file; } - String file = (String)run_context.getProperties().get("File"); - if (file != null) label += " " + file; } result.setLabel(label, 0); } - @Override - ModelDelta makeModelDelta(int flags) { - if (run_context != null && run_context.isContainer()) flags |= IModelDelta.STATE; - return super.makeModelDelta(flags); - } - void onContextAdded(IRunControl.RunControlContext context) { children_exec.onContextAdded(context); } void onContextChanged(IRunControl.RunControlContext context) { assert !disposed; - if (!valid_run_ctx) invalidateNode(); - run_context = context; - valid_run_ctx = true; + run_context.reset(context); resumed_cnt++; children_stack.onSourceMappingChange(); makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); @@ -217,9 +306,7 @@ public class TCFNodeExecContext extends TCFNode { void onContextChanged(IMemory.MemoryContext context) { assert !disposed; - if (!valid_mem_ctx) invalidateNode(); - mem_context = context; - valid_mem_ctx = true; + mem_context.reset(context); resumed_cnt++; makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); } @@ -228,104 +315,70 @@ public class TCFNodeExecContext extends TCFNode { assert !disposed; resumed_cnt++; dispose(); - if (parent instanceof TCFNodeExecContext && - ((TCFNodeExecContext)parent).children_exec.valid) { - makeModelDelta(IModelDelta.REMOVED); - } - else { - parent.invalidateNode(); - parent.makeModelDelta(IModelDelta.CONTENT); - } + parent.makeModelDelta(IModelDelta.CONTENT); } void onContainerSuspended() { assert !disposed; - if (valid_run_ctx) { - if (run_context == null) return; - if (!run_context.hasState()) return; - suspended = false; - running = false; - valid_state = false; - super.invalidateNode(); - children_stack.onSuspended(); - } - else { - invalidateNode(); + if (run_context.isValid()) { + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx == null) return; + if (!ctx.hasState()) return; } + state.reset(); + children_stack.onSuspended(); makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); } void onContainerResumed() { assert !disposed; - if (valid_run_ctx) { - if (run_context == null) return; - if (!run_context.hasState()) return; - suspended = false; - running = false; - valid_state = false; - super.invalidateNode(); - children_stack.onResumed(); - } - else { - invalidateNode(); + if (run_context.isValid()) { + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx == null) return; + if (!ctx.hasState()) return; } + state.reset(); makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); } void onContextSuspended(String pc, String reason, Map<String,Object> params) { assert !disposed; - if (valid_run_ctx) { - if (run_context == null) return; - if (!run_context.hasState()) return; - super.invalidateNode(); - children_stack.onSuspended(); - suspended = true; - suspended_pc = pc; - suspended_reason = reason; - suspended_params = params; - running = false; - valid_state = true; - } - else { - invalidateNode(); - } + ContextState s = new ContextState(); + s.suspended = true; + s.suspended_pc = pc; + s.suspended_reason = reason; + state.reset(s); + children_stack.onSuspended(); resumed_cnt++; makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); } void onContextResumed() { assert !disposed; - if (valid_run_ctx) { - if (run_context == null) return; - if (!run_context.hasState()) return; - super.invalidateNode(); - exception_msg = null; - terminated = false; - suspended = false; - suspended_pc = null; - suspended_reason = null; - suspended_params = null; - running = true; - valid_state = true; - } - else { - invalidateNode(); - } - makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); + state.reset(new ContextState()); + makeModelDelta(IModelDelta.STATE); final int cnt = ++resumed_cnt; model.invokeLater(250, new Runnable() { public void run() { if (cnt != resumed_cnt) return; + if (disposed) return; children_stack.onResumed(); + if (!validateNode(this)) return; makeModelDelta(IModelDelta.CONTENT); + if (parent instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)parent).onChildResumedOrSuspended(); + } } }); } + + void onChildResumedOrSuspended() { + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null && ctx.isContainer()) makeModelDelta(IModelDelta.STATE); + if (parent instanceof TCFNodeExecContext) ((TCFNodeExecContext)parent).onChildResumedOrSuspended(); + } void onContextException(String msg) { - assert !disposed; - exception_msg = msg; - makeModelDelta(IModelDelta.STATE); } void onMemoryChanged(Number[] addr, long[] size) { @@ -333,157 +386,117 @@ public class TCFNodeExecContext extends TCFNode { } void onRegistersChanged() { - super.invalidateNode(); - children_regs.invalidate(); + children_stack.onRegistersChanged(); makeModelDelta(IModelDelta.CONTENT); } @Override public void invalidateNode() { - super.invalidateNode(); - valid_mem_ctx = false; - valid_run_ctx = false; - valid_state = false; - running = false; - suspended = false; - children_exec.invalidate(); - children_stack.invalidate(); - children_regs.invalidate(); + run_context.reset(); + mem_context.reset(); + state.reset(); + children_exec.reset(); + children_stack.reset(); + children_regs.reset(); } @Override - protected boolean validateNodeData() { + public boolean validateNode(Runnable done) { assert !disposed; - if (!valid_mem_ctx && !validateMemoryContext()) return false; - if (!valid_run_ctx && !validateRunControlContext()) return false; - if (!valid_state && !validateRunControlState()) return false; - if (!children_stack.valid && !children_stack.validate()) return false; - if (!children_regs.valid && !children_regs.validate()) return false; - if (!children_exec.valid && !children_exec.validate()) return false; - if (run_context != null && !run_context.hasState()) { - // Container need to validate children for hasSuspendedChildren() method - // to return valid value. - if (!validateNodes(children_exec.children.values())) return false; + mem_context.validate(); + run_context.validate(); + if (!mem_context.isValid()) { + mem_context.wait(done); + return false; } - return true; - } - - private boolean validateMemoryContext() { - assert pending_command == null; - IMemory mem = model.getLaunch().getService(IMemory.class); - if (mem == null) { - valid_mem_ctx = true; - return true; + if (!run_context.isValid()) { + run_context.wait(done); + return false; } - pending_command = mem.getContext(id, new IMemory.DoneGetContext() { - public void doneGetContext(IToken token, Exception error, IMemory.MemoryContext context) { - if (pending_command != token) return; - pending_command = null; - if (error != null) { - node_error = error; - } - else { - mem_context = context; - } - valid_mem_ctx = true; - validateNode(); - } - }); - return false; - } - - private boolean validateRunControlContext() { - assert pending_command == null; - IRunControl run = model.getLaunch().getService(IRunControl.class); - if (run == null) { - valid_run_ctx = true; - return true; + state.validate(); + children_exec.validate(); + if (!state.isValid()) { + state.wait(done); + return false; } - pending_command = run.getContext(id, new IRunControl.DoneGetContext() { - public void doneGetContext(IToken token, Exception error, IRunControl.RunControlContext context) { - if (pending_command != token) return; - pending_command = null; - if (error != null) { - node_error = error; - } - else { - run_context = context; - } - valid_run_ctx = true; - validateNode(); + if (!children_exec.isValid()) { + children_exec.wait(done); + return false; + } + children_regs.validate(); + children_stack.validate(); + + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null && !ctx.hasState()) { + // Container need to validate children for + // hasSuspendedChildren() method to return valid value. + TCFDataCache<?> dt = validateChildrenState(); + if (dt != null) { + dt.wait(done); + return false; } - }); - return false; - } - - private boolean validateRunControlState() { - assert pending_command == null; - if (node_error != null || run_context == null || !run_context.hasState()) { - suspended = false; - suspended_pc = null; - suspended_reason = null; - suspended_params = null; - running = false; - valid_state = true; - return true; } - pending_command = run_context.getState(new IRunControl.DoneGetState() { - public void doneGetState(IToken token, Exception error, boolean suspend, String pc, String reason, Map<String,Object> params) { - if (token != pending_command) return; - pending_command = null; - if (error != null) { - suspended = false; - suspended_pc = null; - suspended_reason = null; - suspended_params = null; - node_error = error; - running = false; - } - else { - suspended = suspend; - if (suspend) { - suspended_pc = pc; - suspended_reason = reason; - suspended_params = params; - } - else { - suspended_pc = null; - suspended_reason = null; - suspended_params = null; - } - running = !suspend; - } - valid_state = true; - validateNode(); + + if (!children_regs.isValid()) { + children_regs.wait(done); + return false; + } + if (!children_stack.isValid()) { + children_stack.wait(done); + return false; + } + return true; + } + + // Validate children state for hasSuspendedChildren() + // Return TCFDataCache to wait for if validation is pending. + private TCFDataCache<?> validateChildrenState() { + if (!children_exec.validate()) return children_exec; + TCFDataCache<?> pending = null; + for (TCFNode n : children_exec.getData().values()) { + if (!(n instanceof TCFNodeExecContext)) continue; + TCFNodeExecContext e = (TCFNodeExecContext)n; + if (!e.run_context.validate()) { + pending = e.run_context; + continue; } - }); - return false; + IRunControl.RunControlContext ctx = e.run_context.getData(); + if (ctx == null) continue; + if (ctx.hasState() && !e.state.validate()) pending = e.state; + if (ctx.isContainer()) pending = e.validateChildrenState(); + } + return pending; } + // Return true if at least one child is suspended + // The method will fail if node is not validated, see validateChildrenState() private boolean hasSuspendedChildren() { - for (TCFNode n : children_exec.children.values()) { - if (n instanceof TCFNodeExecContext) { - TCFNodeExecContext e = (TCFNodeExecContext)n; - if (e.run_context != null) { - if (e.run_context.hasState() && e.suspended) return true; - if (e.run_context.isContainer() && e.hasSuspendedChildren()) return true; - } - } + Map<String,TCFNode> m = children_exec.getData(); + if (m == null) return false; + for (TCFNode n : m.values()) { + if (!(n instanceof TCFNodeExecContext)) continue; + TCFNodeExecContext e = (TCFNodeExecContext)n; + IRunControl.RunControlContext ctx = e.run_context.getData(); + if (ctx == null) continue; + if (ctx.hasState() && e.isSuspended()) return true; + if (ctx.isContainer() && e.hasSuspendedChildren()) return true; } return false; } @Override protected String getImageName() { - if (run_context != null && run_context.hasState()) { + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null && ctx.hasState()) { // Thread - if (terminated) return "icons/full/obj16/threadt_obj.gif"; - if (suspended) return "icons/full/obj16/threads_obj.gif"; + ContextState s = state.getData(); + if (s != null && s.terminated) return "icons/full/obj16/threadt_obj.gif"; + if (s != null && s.suspended) return "icons/full/obj16/threads_obj.gif"; return "icons/full/obj16/thread_obj.gif"; } - else if (run_context != null) { + else if (ctx != null) { // Thread container (process) - if (terminated) return "icons/full/obj16/debugtt_obj.gif"; + //if (terminated) return "icons/full/obj16/debugtt_obj.gif"; if (hasSuspendedChildren()) return "icons/full/obj16/debugts_obj.gif"; return "icons/full/obj16/debugt_obj.gif"; } diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFNodeLaunch.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFNodeLaunch.java index 5e7b8bf65..6b767821d 100644 --- a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFNodeLaunch.java +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFNodeLaunch.java @@ -16,11 +16,8 @@ import org.eclipse.debug.core.DebugPlugin; 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.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.ModelDelta; -import org.eclipse.swt.graphics.RGB; -import org.eclipse.tm.internal.tcf.debug.model.TCFLaunch; import org.eclipse.tm.tcf.services.IMemory; import org.eclipse.tm.tcf.services.IRunControl; @@ -67,26 +64,7 @@ public class TCFNodeLaunch extends TCFNode { protected void getData(IHasChildrenUpdate result) { result.setHasChilren(children.size() > 0); } - - @Override - protected void getData(ILabelUpdate result) { - result.setImageDescriptor(getImageDescriptor(getImageName()), 0); - String label = id; - TCFLaunch launch = model.getLaunch(); - String status = ""; - if (launch.isConnecting()) status = "Connecting"; - else if (launch.isDisconnected()) status = "Disconnected"; - else if (launch.isTerminated()) status = "Terminated"; - Throwable error = launch.getError(); - if (error != null) { - status += " - " + error; - result.setForeground(new RGB(255, 0, 0), 0); - } - if (status.length() > 0) status = " (" + status + ")"; - label = launch.getLaunchConfiguration().getName() + status; - result.setLabel(label, 0); - } - + void onContextAdded(IRunControl.RunControlContext context) { children.onContextAdded(context); } @@ -96,16 +74,14 @@ public class TCFNodeLaunch extends TCFNode { } @Override - ModelDelta makeModelDelta(int flags) { - int count = -1; - ModelDelta delta = model.getDelta(this); + ModelDelta makeModelDelta(TCFModelProxy p, int flags) { + ModelDelta delta = p.getDelta(this); if (delta == null) { delta = new ModelDelta(DebugPlugin.getDefault().getLaunchManager(), IModelDelta.NO_CHANGE); - delta = delta.addNode(model.getLaunch(), -1, flags, count); - model.addDelta(this, delta); + delta = delta.addNode(model.getLaunch(), -1, flags, -1); + p.addDelta(this, delta); } else { - assert delta.getChildCount() == count; delta.setFlags(delta.getFlags() | flags); } return delta; @@ -113,18 +89,15 @@ public class TCFNodeLaunch extends TCFNode { @Override public void invalidateNode() { - super.invalidateNode(); - children.invalidate(); + children.reset(); } @Override - protected boolean validateNodeData() { - if (!children.valid && !children.validate()) return false; + public boolean validateNode(Runnable done) { + if (!children.validate()) { + children.wait(done); + return false; + } return true; } - - @Override - protected String getImageName() { - return "icons/full/obj16/ldebug_obj.gif"; - } } diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFNodeRegister.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFNodeRegister.java index 748bd7280..575d3ea14 100644 --- a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFNodeRegister.java +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFNodeRegister.java @@ -12,6 +12,11 @@ package org.eclipse.tm.internal.tcf.debug.ui.model; 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.IPresentationContext; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.tm.internal.tcf.debug.ui.ImageCache; +import org.eclipse.tm.tcf.protocol.IChannel; import org.eclipse.tm.tcf.protocol.IToken; import org.eclipse.tm.tcf.services.IRegisters; @@ -37,41 +42,114 @@ public class TCFNodeRegister extends TCFNode { COL_MNEMONIC = "Menimonic"; - private IRegisters.RegistersContext context; - private String hex_value; - private String dec_value; - private Number num_value; - private boolean valid_context; - private boolean valid_hex_value; - private boolean valid_dec_value; + private final TCFDataCache<IRegisters.RegistersContext> context; + private final TCFDataCache<String> hex_value; + private final TCFDataCache<String> dec_value; - TCFNodeRegister(TCFNode parent, String id) { + TCFNodeRegister(TCFNode parent, final String id) { super(parent, id); + IChannel channel = parent.model.getLaunch().getChannel(); + context = new TCFDataCache<IRegisters.RegistersContext>(channel) { + @Override + protected boolean startDataRetrieval() { + IRegisters regs = model.getLaunch().getService(IRegisters.class); + command = regs.getContext(id, new IRegisters.DoneGetContext() { + public void doneGetContext(IToken token, Exception error, IRegisters.RegistersContext context) { + set(token, error, context); + } + }); + return false; + } + }; + hex_value = new TCFDataCache<String>(channel) { + @Override + protected boolean startDataRetrieval() { + if (!context.validate()) { + context.wait(this); + return false; + } + IRegisters.RegistersContext ctx = context.getData(); + if (ctx == null) { + set(null, null, null); + return true; + } + String[] fmts = ctx.getAvailableFormats(); + String fmt = null; + for (String s : fmts) { + if (s.equals(IRegisters.FORMAT_HEX)) fmt = s; + } + if (fmt == null) { + set(null, null, null); + return true; + } + command = ctx.get(fmt, new IRegisters.DoneGet() { + public void doneGet(IToken token, Exception error, String value) { + set(token, error, value); + } + }); + return false; + } + }; + dec_value = new TCFDataCache<String>(channel) { + @Override + protected boolean startDataRetrieval() { + if (!context.validate()) { + context.wait(this); + return false; + } + IRegisters.RegistersContext ctx = context.getData(); + if (ctx == null) { + set(null, null, null); + return true; + } + String[] fmts = ctx.getAvailableFormats(); + String fmt = null; + for (String s : fmts) { + if (s.equals(IRegisters.FORMAT_DECIMAL)) fmt = s; + } + if (fmt == null) { + set(null, null, null); + return true; + } + command = ctx.get(fmt, new IRegisters.DoneGet() { + public void doneGet(IToken token, Exception error, String value) { + set(token, error, value); + } + }); + return false; + } + }; } @Override protected void getData(ILabelUpdate result) { - result.setImageDescriptor(getImageDescriptor(getImageName()), 0); - if (context != null) { + result.setImageDescriptor(ImageCache.getImageDescriptor(getImageName()), 0); + IRegisters.RegistersContext ctx = context.getData(); + Throwable error = context.getError(); + if (error != null) { + result.setForeground(new RGB(255, 0, 0), 0); + result.setLabel(id + ": " + error.getClass().getName() + ": " + error.getMessage(), 0); + } + else if (ctx != null) { String[] cols = result.getColumnIds(); if (cols == null) { - result.setLabel(context.getName() + " = " + hex_value, 0); + result.setLabel(ctx.getName() + " = " + hex_value.getData(), 0); } else { for (int i = 0; i < cols.length; i++) { String c = cols[i]; - if (c.equals(COL_NAME)) result.setLabel(context.getName(), i); - else if (c.equals(COL_HEX_VALUE)) result.setLabel(hex_value, i); - else if (c.equals(COL_DEC_VALUE)) result.setLabel(dec_value, i); - else if (c.equals(COL_DESCRIPTION)) result.setLabel(context.getDescription(), i); - else if (c.equals(COL_READBLE)) result.setLabel(bool(context.isReadable()), i); - else if (c.equals(COL_READ_ONCE)) result.setLabel(bool(context.isReadOnce()), i); - else if (c.equals(COL_WRITEABLE)) result.setLabel(bool(context.isWriteable()), i); - else if (c.equals(COL_WRITE_ONCE)) result.setLabel(bool(context.isWriteOnce()), i); - else if (c.equals(COL_SIDE_EFFECTS)) result.setLabel(bool(context.hasSideEffects()), i); - else if (c.equals(COL_VOLATILE)) result.setLabel(bool(context.isVolatile()), i); - else if (c.equals(COL_FLOAT)) result.setLabel(bool(context.isFloat()), i); - else if (c.equals(COL_MNEMONIC)) result.setLabel(getMnemonic(), i); + if (c.equals(COL_NAME)) result.setLabel(ctx.getName(), i); + else if (c.equals(COL_HEX_VALUE)) setLabel(result, hex_value, i); + else if (c.equals(COL_DEC_VALUE)) result.setLabel(dec_value.getData(), i); + else if (c.equals(COL_DESCRIPTION)) result.setLabel(ctx.getDescription(), i); + else if (c.equals(COL_READBLE)) result.setLabel(bool(ctx.isReadable()), i); + else if (c.equals(COL_READ_ONCE)) result.setLabel(bool(ctx.isReadOnce()), i); + else if (c.equals(COL_WRITEABLE)) result.setLabel(bool(ctx.isWriteable()), i); + else if (c.equals(COL_WRITE_ONCE)) result.setLabel(bool(ctx.isWriteOnce()), i); + else if (c.equals(COL_SIDE_EFFECTS)) result.setLabel(bool(ctx.hasSideEffects()), i); + else if (c.equals(COL_VOLATILE)) result.setLabel(bool(ctx.isVolatile()), i); + else if (c.equals(COL_FLOAT)) result.setLabel(bool(ctx.isFloat()), i); + else if (c.equals(COL_MNEMONIC)) result.setLabel(getMnemonic(ctx), i); } } } @@ -79,30 +157,59 @@ public class TCFNodeRegister extends TCFNode { result.setLabel(id, 0); } } + + private void setLabel(ILabelUpdate result, TCFDataCache<String> data, int pos) { + Throwable error = data.getError(); + if (error != null) { + result.setForeground(new RGB(255, 0, 0), pos); + result.setLabel(error.getClass().getName() + ": " + error.getMessage(), pos); + } + else if (data.getData() != null) { + result.setLabel(data.getData(), pos); + } + } private String bool(boolean b) { return b ? "yes" : "no"; } - private String getMnemonic() { - if (num_value != null) { - IRegisters.NamedValue[] arr = context.getNamedValues(); + private String getMnemonic(IRegisters.RegistersContext ctx) { + if (dec_value.getData() != null) { + IRegisters.NamedValue[] arr = ctx.getNamedValues(); if (arr != null) { - if (context.isFloat()) { + if (ctx.isFloat()) { + double v = Double.parseDouble(dec_value.getData()); for (IRegisters.NamedValue n : arr) { - if (n.getValue().doubleValue() == num_value.doubleValue()) return n.getName(); + if (n.getValue().doubleValue() == v) return n.getName(); } } else { + long v = Long.parseLong(dec_value.getData()); for (IRegisters.NamedValue n : arr) { - if (n.getValue().longValue() == num_value.longValue()) return n.getName(); + if (n.getValue().longValue() == v) return n.getName(); } } } } + else if (!ctx.isFloat() && hex_value.getData() != null) { + IRegisters.NamedValue[] arr = ctx.getNamedValues(); + if (arr != null) { + long v = Long.parseLong(hex_value.getData(), 16); + for (IRegisters.NamedValue n : arr) { + if (n.getValue().longValue() == v) return n.getName(); + } + } + } return ""; } + int getRelevantModelDeltaFlags(IPresentationContext p) { + if (IDebugUIConstants.ID_REGISTER_VIEW.equals(p.getId())) { + return IModelDelta.CONTENT | IModelDelta.STATE; + } + return 0; + } + void onValueChanged() { onSuspended(); } @@ -111,111 +218,43 @@ public class TCFNodeRegister extends TCFNode { * Invalidate register value only, keep cached register attributes. */ void onSuspended() { - super.invalidateNode(); - valid_hex_value = false; - valid_dec_value = false; - hex_value = null; - dec_value = null; - num_value = null; + hex_value.reset(); + dec_value.reset(); makeModelDelta(IModelDelta.STATE); } + + void onRegistersChanged() { + context.reset(); + hex_value.reset(); + dec_value.reset(); + makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); + } @Override public void invalidateNode() { - super.invalidateNode(); - valid_context = false; - valid_hex_value = false; - valid_dec_value = false; - hex_value = null; - dec_value = null; - num_value = null; + context.reset(); + hex_value.reset(); + dec_value.reset(); } @Override - protected boolean validateNodeData() { - if (!valid_context && !validateRegisterContext()) return false; - if (!valid_hex_value && !validateRegisterHexValue()) return false; - if (!valid_dec_value && !validateRegisterDecValue()) return false; - return true; - } - - private boolean validateRegisterContext() { - assert pending_command == null; - IRegisters regs = model.getLaunch().getService(IRegisters.class); - pending_command = regs.getContext(id, new IRegisters.DoneGetContext() { - public void doneGetContext(IToken token, Exception error, IRegisters.RegistersContext context) { - if (pending_command != token) return; - pending_command = null; - if (error != null) { - node_error = error; - } - else { - TCFNodeRegister.this.context = context; - } - valid_context = true; - validateNode(); - } - }); - return false; - } - - private boolean validateRegisterHexValue() { - assert pending_command == null; - String[] fmts = context.getAvailableFormats(); - String fmt = null; - for (String s : fmts) { - if (s.equals(IRegisters.FORMAT_HEX)) fmt = s; + public boolean validateNode(Runnable done) { + boolean ctx_valid = context.validate(); + boolean dec_valid = dec_value.validate(); + boolean hex_valid = hex_value.validate(); + if (!ctx_valid) { + if (done != null) context.wait(done); + return false; } - if (fmt == null) { - valid_hex_value = true; - return true; + if (!dec_valid) { + if (done != null) dec_value.wait(done); + return false; } - pending_command = context.get(fmt, new IRegisters.DoneGet() { - public void doneGet(IToken token, Exception error, String value) { - if (pending_command != token) return; - pending_command = null; - if (error != null) { - node_error = error; - } - else { - hex_value = value; - if (!context.isFloat()) num_value = Long.valueOf(value, 16); - } - valid_hex_value = true; - validateNode(); - } - }); - return false; - } - - private boolean validateRegisterDecValue() { - assert pending_command == null; - String[] fmts = context.getAvailableFormats(); - String fmt = null; - for (String s : fmts) { - if (s.equals(IRegisters.FORMAT_DECIMAL)) fmt = s; - } - if (fmt == null) { - valid_dec_value = true; - return true; + if (!hex_valid) { + if (done != null) hex_value.wait(done); + return false; } - pending_command = context.get(fmt, new IRegisters.DoneGet() { - public void doneGet(IToken token, Exception error, String value) { - if (pending_command != token) return; - pending_command = null; - if (error != null) { - node_error = error; - } - else { - dec_value = value; - if (!context.isFloat()) num_value = Long.valueOf(value, 10); - else num_value = Double.valueOf(value); - } - valid_dec_value = true; - validateNode(); - } - }); - return false; + return true; } @Override diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFNodeStackFrame.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFNodeStackFrame.java index 41928e60b..71c037ed2 100644 --- a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFNodeStackFrame.java +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFNodeStackFrame.java @@ -11,8 +11,8 @@ package org.eclipse.tm.internal.tcf.debug.ui.model; import java.math.BigInteger; -import java.util.ArrayList; import java.util.Arrays; +import java.util.Map; import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; @@ -20,6 +20,9 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdat 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.IDebugUIConstants; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.tm.internal.tcf.debug.ui.ImageCache; +import org.eclipse.tm.tcf.protocol.IChannel; import org.eclipse.tm.tcf.protocol.IToken; import org.eclipse.tm.tcf.protocol.Protocol; import org.eclipse.tm.tcf.services.ILineNumbers; @@ -28,31 +31,108 @@ import org.eclipse.tm.tcf.services.IRunControl; import org.eclipse.tm.tcf.services.IStackTrace; import org.eclipse.tm.tcf.services.ILineNumbers.CodeArea; - +@SuppressWarnings("serial") public class TCFNodeStackFrame extends TCFNode { - private IStackTrace.StackTraceContext stack_trace_context; - private ILineNumbers.CodeArea code_area; - private BigInteger code_address; - - private final int frame_no; + private int frame_no; private final TCFChildrenRegisters children_regs; - - TCFNodeStackFrame(TCFNode parent, String id, TCFChildrenRegisters children_regs) { + private final TCFDataCache<IStackTrace.StackTraceContext> stack_trace_context; + private final TCFDataCache<TCFSourceRef> line_info; + + private TCFNodeStackFrame(final TCFNodeExecContext parent, final String id, final int frame_no, TCFChildrenRegisters regs) { super(parent, id); - this.frame_no = 0; - this.children_regs = children_regs; + this.frame_no = frame_no; + if (regs == null) regs = new TCFChildrenRegisters(this); + this.children_regs = regs; + IChannel channel = model.getLaunch().getChannel(); + stack_trace_context = new TCFDataCache<IStackTrace.StackTraceContext>(channel) { + @Override + protected boolean startDataRetrieval() { + assert command == null; + if (frame_no == 0) { + set(null, null, null); + return true; + } + IStackTrace st = model.getLaunch().getService(IStackTrace.class); + command = st.getContext(new String[]{ id }, new IStackTrace.DoneGetContext() { + public void doneGetContext(IToken token, Exception error, IStackTrace.StackTraceContext[] context) { + set(token, error, context[0]); + } + }); + return false; + } + }; + final Map<BigInteger,TCFSourceRef> line_info_cache = parent.getLineInfoCache(); + line_info = new TCFDataCache<TCFSourceRef>(channel) { + @Override + protected boolean startDataRetrieval() { + BigInteger n = null; + if (frame_no == 0) { + if (!parent.validateNode(this)) return false; + } + else { + if (!stack_trace_context.validate()) { + stack_trace_context.wait(this); + return false; + } + } + String s = getAddress(); + if (s != null) n = new BigInteger(s); + if (n == null) { + set(null, null, null); + return true; + } + TCFSourceRef l = line_info_cache.get(n); + if (l != null) { + set(null, null, l); + return true; + } + ILineNumbers ln = model.getLaunch().getService(ILineNumbers.class); + if (ln == null) { + l = new TCFSourceRef(); + l.address = n; + set(null, null, l); + return true; + } + final BigInteger n0 = n; + final BigInteger n1 = n0.add(BigInteger.valueOf(1)); + command = ln.mapToSource(parent.id, n0, n1, new ILineNumbers.DoneMapToSource() { + public void doneMapToSource(IToken token, Exception error, CodeArea[] areas) { + TCFSourceRef l = new TCFSourceRef(); + l.address = n0; + if (error == null && areas != null && areas.length > 0) { + for (ILineNumbers.CodeArea area : areas) { + if (l.area == null || area.start_line < l.area.start_line) { + l.area = area; + } + } + } + l.error = error; + set(token, null, l); + if (error == null) line_info_cache.put(l.address, l); + } + }); + return false; + } + }; } - TCFNodeStackFrame(TCFNode parent, String id, int frame_no) { - super(parent, id); - this.frame_no = frame_no; - children_regs = new TCFChildrenRegisters(this); + TCFNodeStackFrame(TCFNodeExecContext parent, String id, TCFChildrenRegisters children_regs) { + this(parent, id, 0, children_regs); + } + + TCFNodeStackFrame(TCFNodeExecContext parent, String id, int frame_no) { + this(parent, id, frame_no, null); } int getFrameNo() { return frame_no; } + + void setFrameNo(int frame_no) { + assert this.frame_no != 0 && frame_no != 0; + this.frame_no = frame_no; + } @Override void dispose() { @@ -89,13 +169,15 @@ public class TCFNodeStackFrame extends TCFNode { public String getAddress() { assert Protocol.isDispatchThread(); if (frame_no == 0) return parent.getAddress(); - if (stack_trace_context != null) { - Number addr = stack_trace_context.getReturnAddress(); + if (!stack_trace_context.isValid()) return null; + IStackTrace.StackTraceContext ctx = stack_trace_context.getData(); + if (ctx != null) { + Number addr = ctx.getReturnAddress(); if (addr != null) return addr.toString(); } return null; } - + @Override protected void getData(IChildrenCountUpdate result) { if (IDebugUIConstants.ID_REGISTER_VIEW.equals(result.getPresentationContext().getId())) { @@ -139,25 +221,26 @@ public class TCFNodeStackFrame extends TCFNode { @Override protected void getData(ILabelUpdate result) { - result.setImageDescriptor(getImageDescriptor(getImageName()), 0); - String label = id; - Number n = null; - if (frame_no == 0 && parent.getAddress() != null) { - n = new BigInteger(parent.getAddress()); - } - else if (stack_trace_context != null) { - n = stack_trace_context.getReturnAddress(); - } - if (n == null) { - label = "..."; + result.setImageDescriptor(ImageCache.getImageDescriptor(getImageName()), 0); + Throwable error = stack_trace_context.getError(); + if (error == null) error = line_info.getError(); + if (error != null) { + result.setForeground(new RGB(255, 0, 0), 0); + result.setLabel(error.getClass().getName() + ": " + error.getMessage(), 0); } else { - label = makeHexAddrString(n); - if (code_area != null && code_area.file != null) { - label += ": " + code_area.file + ", line " + (code_area.start_line + 1); + TCFSourceRef l = line_info.getData(); + if (l == null) { + result.setLabel("...", 0); + } + else { + String label = makeHexAddrString(l.address); + if (l.area != null && l.area.file != null) { + label += ": " + l.area.file + ", line " + (l.area.start_line + 1); + } + result.setLabel(label, 0); } } - result.setLabel(label, 0); } private String makeHexAddrString(Number n) { @@ -174,97 +257,47 @@ public class TCFNodeStackFrame extends TCFNode { } void onSourceMappingChange() { - super.invalidateNode(); - code_address = null; - code_area = null; + line_info.reset(); makeModelDelta(IModelDelta.STATE); } void onSuspended() { - super.invalidateNode(); - stack_trace_context = null; + stack_trace_context.reset(); + line_info.reset(); children_regs.onSuspended(); makeModelDelta(IModelDelta.STATE); } + + void onRegistersChanged() { + children_regs.onRegistersChanged(); + makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); + } @Override public void invalidateNode() { - super.invalidateNode(); - stack_trace_context = null; - code_address = null; - code_area = null; - children_regs.invalidate(); + stack_trace_context.reset(); + line_info.reset(); + children_regs.reset(); } @Override - protected boolean validateNodeData() { - assert pending_command == null; - if (node_error != null) return true; - if (frame_no == 0) { - if (!children_regs.valid) { - assert children_regs.node == parent; - // Need to validate parent for children_regs to be valid. - ArrayList<TCFNode> nodes = new ArrayList<TCFNode>(); - nodes.add(parent); - if (!validateNodes(nodes)) return false; - } - return validateSourceMapping(); + public boolean validateNode(Runnable done) { + if (frame_no == 0 && !parent.validateNode(done)) return false; + stack_trace_context.validate(); + children_regs.validate(); + if (!stack_trace_context.isValid()) { + stack_trace_context.wait(done); + return false; } - if (!children_regs.valid && !children_regs.validate()) return false; - if (stack_trace_context != null) return true; - IStackTrace st = model.getLaunch().getService(IStackTrace.class); - pending_command = st.getContext(new String[]{ id }, new IStackTrace.DoneGetContext() { - public void doneGetContext(IToken token, Exception error, IStackTrace.StackTraceContext[] context) { - if (pending_command != token) return; - pending_command = null; - if (error != null) { - node_error = error; - } - else { - stack_trace_context = context[0]; - } - if (!validateSourceMapping()) return; - validateNode(); - } - }); - return false; - } - - private boolean validateSourceMapping() { - BigInteger n = null; - ILineNumbers ln = model.getLaunch().getService(ILineNumbers.class); - if (node_error == null && ln != null) { - String s = getAddress(); - if (s != null) n = new BigInteger(s); + if (!children_regs.isValid()) { + children_regs.wait(done); + return false; } - if (n != null && n.equals(code_address)) return true; - if (n == null) { - code_area = null; - code_address = null; - return true; + if (!line_info.validate()) { + line_info.wait(done); + return false; } - final BigInteger n0 = n; - final BigInteger n1 = n0.add(BigInteger.valueOf(1)); - pending_command = ln.mapToSource(parent.id, n0, n1, new ILineNumbers.DoneMapToSource() { - public void doneMapToSource(IToken token, Exception error, CodeArea[] areas) { - if (pending_command != token) return; - pending_command = null; - code_area = null; - if (error != null) { - node_error = error; - } - else if (areas != null && areas.length > 0) { - for (ILineNumbers.CodeArea area : areas) { - if (code_area == null || area.start_line < code_area.start_line) { - code_area = area; - } - } - } - code_address = n0; - validateNode(); - } - }); - return false; + return true; } @Override diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFSourceRef.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFSourceRef.java new file mode 100644 index 000000000..e8e213406 --- /dev/null +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFSourceRef.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2008 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.math.BigInteger; + +import org.eclipse.tm.tcf.services.ILineNumbers; + +/** + * Objects of this class represent a mapping between an address and source code area. + */ +class TCFSourceRef { + BigInteger address; + ILineNumbers.CodeArea area; + Throwable error; +} |