diff options
author | eutarass | 2008-06-16 23:33:58 +0000 |
---|---|---|
committer | eutarass | 2008-06-16 23:33:58 +0000 |
commit | c4534274267e6b8a0aa2a7d0f0f5e9348d04909d (patch) | |
tree | a2e893e0f71998b8765e703687c01ff81302a496 | |
parent | 870c5a56a7a83e7d1733a9b853d1d7d416c604ee (diff) | |
download | org.eclipse.tcf-c4534274267e6b8a0aa2a7d0f0f5e9348d04909d.tar.gz org.eclipse.tcf-c4534274267e6b8a0aa2a7d0f0f5e9348d04909d.tar.xz org.eclipse.tcf-c4534274267e6b8a0aa2a7d0f0f5e9348d04909d.zip |
1. TCF agent Stack Trace service: fixed handling of PLT entries on Linux.
2. TCF agent Run Control service: fixed suspend reason reporting
3. TCF debugger: implemented source level step into command
4. Multiple fixes/improvement related to source level stepping
5. Better handling of TCF plugins activation/deactivation: start TCF event thread after plugin is fully activated, shutdown the thread when plugin is deactivated
30 files changed, 1422 insertions, 556 deletions
diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/Activator.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/Activator.java index 50697c1dd..55817ebd0 100644 --- a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/Activator.java +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/Activator.java @@ -14,6 +14,7 @@ import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.tm.internal.tcf.debug.ui.model.TCFAnnotationManager; import org.eclipse.tm.internal.tcf.debug.ui.model.TCFModelManager; +import org.eclipse.tm.tcf.protocol.Protocol; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; @@ -26,10 +27,8 @@ import org.osgi.framework.BundleListener; */ public class Activator extends AbstractUIPlugin { - // The plug-in ID public static final String PLUGIN_ID = "org.eclipse.tm.tcf.debug.ui"; - // The shared instance private static Activator plugin; private static TCFModelManager model_manager; private static TCFAnnotationManager annotation_manager; @@ -38,14 +37,18 @@ public class Activator extends AbstractUIPlugin { public void bundleChanged(BundleEvent event) { if (plugin != null && event.getBundle() == plugin.getBundle() && plugin.getBundle().getState() != Bundle.ACTIVE) { - if (model_manager != null) { - model_manager.dispose(); - model_manager = null; - } - if (annotation_manager != null) { - annotation_manager.dispose(); - annotation_manager = null; - } + Protocol.invokeAndWait(new Runnable() { + public void run() { + if (model_manager != null) { + model_manager.dispose(); + model_manager = null; + } + if (annotation_manager != null) { + annotation_manager.dispose(); + annotation_manager = null; + } + } + }); } } }; @@ -64,6 +67,13 @@ public class Activator extends AbstractUIPlugin { public void start(BundleContext context) throws Exception { super.start(context); context.addBundleListener(bundle_listener); + Protocol.invokeLater(new Runnable() { + public void run() { + if (plugin == null) return; + model_manager = new TCFModelManager(); + annotation_manager = new TCFAnnotationManager(); + } + }); } /* @@ -90,9 +100,6 @@ public class Activator extends AbstractUIPlugin { * @return the shared TCFModelManager instance */ public static TCFModelManager getModelManager() { - if (plugin != null && model_manager == null && plugin.getBundle().getState() == Bundle.ACTIVE) { - model_manager = new TCFModelManager(); - } return model_manager; } @@ -102,9 +109,6 @@ public class Activator extends AbstractUIPlugin { * @return the shared TCFAnnotationManager instance */ public static TCFAnnotationManager getAnnotationManager() { - if (plugin != null && annotation_manager == null && plugin.getBundle().getState() == Bundle.ACTIVE) { - annotation_manager = new TCFAnnotationManager(); - } return annotation_manager; } 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 index 3ffaf94b3..f7ec48a99 100644 --- 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 @@ -25,11 +25,14 @@ class TCFLaunchLabelProvider implements IElementLabelProvider { result.setImageDescriptor(ImageCache.getImageDescriptor(ImageCache.IMG_TCF), 0); String status = ""; if (launch.isConnecting()) status = "Connecting"; - else if (launch.isDisconnected()) status = "Disconnected"; + else if (launch.isExited()) status = "Exited"; else if (launch.isTerminated()) status = "Terminated"; + else if (launch.isDisconnected()) status = "Disconnected"; Throwable error = launch.getError(); if (error != null) { - status += " - " + error; + String msg = error.getLocalizedMessage(); + if (msg == null || msg.length() == 0) msg = error.getClass().getName(); + status += " - " + msg; result.setForeground(new RGB(255, 0, 0), 0); } if (status.length() > 0) status = " (" + status + ")"; diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/commands/BreakpointCommand.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/commands/BreakpointCommand.java index fceba9f2d..a2b84a8c0 100644 --- a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/commands/BreakpointCommand.java +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/commands/BreakpointCommand.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.tm.internal.tcf.debug.ui.commands; +import java.math.BigInteger; import java.util.HashMap; import java.util.Map; @@ -54,11 +55,11 @@ public class BreakpointCommand implements IToggleBreakpointsTargetExtension { Protocol.invokeAndWait(new Runnable() { public void run() { try { - String addr = node.getAddress(); + BigInteger addr = node.getAddress(); if (addr == null) return; Map<String,Object> m = new HashMap<String,Object>(); m.put(IBreakpoints.PROP_ENABLED, Boolean.TRUE); - m.put(IBreakpoints.PROP_LOCATION, addr); + m.put(IBreakpoints.PROP_LOCATION, addr.toString()); new TCFBreakpoint(ResourcesPlugin.getWorkspace().getRoot(), m); } catch (CoreException x) { diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/commands/StepCommand.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/commands/StepCommand.java new file mode 100644 index 000000000..07292e45d --- /dev/null +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/commands/StepCommand.java @@ -0,0 +1,127 @@ +/******************************************************************************* + * Copyright (c) 2007, 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.commands; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.commands.IDebugCommandRequest; +import org.eclipse.debug.core.commands.IEnabledStateRequest; +import org.eclipse.debug.core.commands.IStepOverHandler; +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.tm.internal.tcf.debug.ui.Activator; +import org.eclipse.tm.internal.tcf.debug.ui.model.TCFModel; +import org.eclipse.tm.internal.tcf.debug.ui.model.TCFNode; +import org.eclipse.tm.internal.tcf.debug.ui.model.TCFRunnable; +import org.eclipse.tm.tcf.protocol.Protocol; +import org.eclipse.tm.tcf.services.IRunControl; + +abstract class StepCommand implements IStepOverHandler { + + protected final TCFModel model; + + public StepCommand(TCFModel model) { + this.model = model; + } + + protected abstract boolean canExecute(IRunControl.RunControlContext ctx); + + protected abstract void execute(IDebugCommandRequest monitor, + IRunControl.RunControlContext ctx, boolean src_step, Runnable done); + + public final void canExecute(final IEnabledStateRequest monitor) { + new TCFRunnable(model.getDisplay(), monitor) { + public void run() { + Object[] elements = monitor.getElements(); + boolean res = false; + for (int i = 0; i < elements.length; i++) { + TCFNode node = null; + if (elements[i] instanceof TCFNode) node = (TCFNode)elements[i]; + else node = model.getRootNode(); + while (node != null && !node.isDisposed()) { + if (!node.validateNode(this)) return; + IRunControl.RunControlContext ctx = node.getRunContext(); + if (!canExecute(ctx)) { + node = node.getParent(); + } + else { + if (node.isSuspended()) res = true; + node = null; + } + } + } + monitor.setEnabled(res); + monitor.setStatus(Status.OK_STATUS); + done(); + } + }; + } + + public final boolean execute(final IDebugCommandRequest monitor) { + new TCFRunnable(model.getDisplay(), monitor) { + public void run() { + Object[] elements = monitor.getElements(); + final Set<IRunControl.RunControlContext> set = new HashSet<IRunControl.RunControlContext>(); + for (int i = 0; i < elements.length; i++) { + TCFNode node = null; + if (elements[i] instanceof TCFNode) node = (TCFNode)elements[i]; + else node = model.getRootNode(); + while (node != null && !node.isDisposed()) { + if (!node.validateNode(this)) return; + IRunControl.RunControlContext ctx = node.getRunContext(); + if (!canExecute(ctx)) { + node = node.getParent(); + } + else { + set.add(ctx); + node = null; + } + } + } + execute(monitor, this, set); + } + }; + return true; + } + + private void execute(final IDebugCommandRequest monitor, final TCFRunnable request, + final Set<IRunControl.RunControlContext> set) { + int i = 0; + final String[] ids = new String[set.size()]; + for (IRunControl.RunControlContext ctx : set) ids[i++] = ctx.getID(); + model.getDisplay().asyncExec(new Runnable() { + public void run() { + boolean src = false; + for (String id : ids) { + Annotation a = Activator.getAnnotationManager().findAnnotation(model, id); + if (a != null) src = true; + } + final boolean src_step = src; + Protocol.invokeLater(new Runnable() { + public void run() { + final Set<Runnable> wait_list = new HashSet<Runnable>(); + for (IRunControl.RunControlContext ctx : set) { + Runnable done = new Runnable() { + public void run() { + wait_list.remove(this); + if (wait_list.isEmpty()) request.done(); + } + }; + wait_list.add(done); + execute(monitor, ctx, src_step, done); + } + } + }); + } + }); + } +} diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/commands/StepIntoCommand.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/commands/StepIntoCommand.java index 279dac6dc..941100bf1 100644 --- a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/commands/StepIntoCommand.java +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/commands/StepIntoCommand.java @@ -10,97 +10,244 @@ *******************************************************************************/ package org.eclipse.tm.internal.tcf.debug.ui.commands; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; +import java.math.BigInteger; +import java.util.Map; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.commands.IDebugCommandRequest; -import org.eclipse.debug.core.commands.IEnabledStateRequest; import org.eclipse.debug.core.commands.IStepIntoHandler; import org.eclipse.tm.internal.tcf.debug.ui.Activator; import org.eclipse.tm.internal.tcf.debug.ui.model.TCFModel; -import org.eclipse.tm.internal.tcf.debug.ui.model.TCFNode; -import org.eclipse.tm.internal.tcf.debug.ui.model.TCFRunnable; +import org.eclipse.tm.internal.tcf.debug.ui.model.TCFNodeExecContext; +import org.eclipse.tm.internal.tcf.debug.ui.model.TCFNodeStackFrame; +import org.eclipse.tm.internal.tcf.debug.ui.model.TCFSourceRef; import org.eclipse.tm.tcf.protocol.IToken; +import org.eclipse.tm.tcf.protocol.Protocol; +import org.eclipse.tm.tcf.services.ILineNumbers; import org.eclipse.tm.tcf.services.IRunControl; +import org.eclipse.tm.tcf.services.IRunControl.RunControlContext; -public class StepIntoCommand implements IStepIntoHandler { +public class StepIntoCommand extends StepCommand implements IStepIntoHandler { - private final TCFModel model; - - public StepIntoCommand(TCFModel model) { - this.model = model; - } - - public void canExecute(final IEnabledStateRequest monitor) { - new TCFRunnable(model.getDisplay(), monitor) { - public void run() { - Object[] elements = monitor.getElements(); - boolean res = false; - for (int i = 0; i < elements.length; i++) { - TCFNode node = null; - if (elements[i] instanceof TCFNode) node = (TCFNode)elements[i]; - else node = model.getRootNode(); - while (node != null && !node.isDisposed()) { - if (!node.validateNode(this)) return; - IRunControl.RunControlContext ctx = node.getRunContext(); - if (ctx == null || !ctx.canResume(IRunControl.RM_STEP_INTO)) { - node = node.getParent(); - } - else { - if (node.isSuspended()) res = true; - node = null; + private static final long TIMEOUT = 10000; + + private static class StepStateMachine extends TCFModel.ContextAction implements IRunControl.RunControlListener { + + private final IDebugCommandRequest monitor; + private final boolean src_step; + private final Runnable done; + private final IRunControl rc = model.getLaunch().getService(IRunControl.class); + + private IRunControl.RunControlContext ctx; + private TCFNodeExecContext node; + private TCFNodeStackFrame frame; + private TCFSourceRef line_info; + private BigInteger pc0; + private BigInteger pc1; + private int step_cnt; + private boolean started; + private boolean exited; + + StepStateMachine(TCFModel model, IDebugCommandRequest monitor, + IRunControl.RunControlContext ctx, + boolean src_step, Runnable done) { + super(model, ctx.getID()); + this.monitor = monitor; + this.ctx = ctx; + this.src_step = src_step; + this.done = done; + } + + public void run() { + assert !exited; + if (!started) { + started = true; + rc.addListener(this); + node = (TCFNodeExecContext)model.getNode(ctx.getID()); + if (node == null) { + exit(new Exception("Invalid context ID")); + return; + } + if (!ctx.canResume(src_step ? IRunControl.RM_STEP_INTO_LINE : IRunControl.RM_STEP_INTO)) { + model.invokeLater(TIMEOUT, new Runnable() { + public void run() { + exit(new Exception("Time out")); } + }); + } + } + if (!node.validateNode(this)) return; + if (!node.isSuspended()) { + exit(new Exception("Context is not suspended")); + return; + } + if (ctx.canResume(src_step ? IRunControl.RM_STEP_INTO_LINE : IRunControl.RM_STEP_INTO)) { + if (step_cnt > 0) return; + ctx.resume(src_step ? IRunControl.RM_STEP_INTO_LINE : IRunControl.RM_STEP_INTO, 1, new IRunControl.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + exit(error); } + }); + step_cnt++; + return; + } + if (!src_step) { + exit(new Exception("Step into is not supported")); + return; + } + if (frame == null) { + frame = node.getTopFrame(); + if (frame == null) { + exit(new Exception("Cannot get top stack frame")); + return; } - monitor.setEnabled(res); - monitor.setStatus(Status.OK_STATUS); - done(); } - }; - } - - public boolean execute(final IDebugCommandRequest monitor) { - new TCFRunnable(model.getDisplay(), monitor) { - public void run() { - Object[] elements = monitor.getElements(); - Set<IRunControl.RunControlContext> set = new HashSet<IRunControl.RunControlContext>(); - for (int i = 0; i < elements.length; i++) { - TCFNode node = null; - if (elements[i] instanceof TCFNode) node = (TCFNode)elements[i]; - else node = model.getRootNode(); - while (node != null && !node.isDisposed()) { - if (!node.validateNode(this)) return; - IRunControl.RunControlContext ctx = node.getRunContext(); - if (ctx == null || !ctx.canResume(IRunControl.RM_STEP_INTO)) { - node = node.getParent(); + if (frame.getFrameNo() >= 0 && !frame.validateNode(this)) return; + if (line_info == null) { + assert frame.getFrameNo() == 0; + line_info = frame.getLineInfo().getData(); + if (line_info == null) { + exit(new Exception("Line info not available")); + return; + } + if (line_info.error != null) { + exit(line_info.error); + return; + } + ILineNumbers.CodeArea area = line_info.area; + if (area != null) { + if (area.start_address instanceof BigInteger) pc0 = (BigInteger)area.start_address; + else if (area.start_address != null) pc0 = new BigInteger(area.start_address.toString()); + if (area.end_address instanceof BigInteger) pc1 = (BigInteger)area.end_address; + else if (area.end_address != null) pc1 = new BigInteger(area.end_address.toString()); + } + } + if (frame.getFrameNo() != 0) { + // Stepped out of selected function + exit(null); + return; + } + if (step_cnt > 0) { + BigInteger pc = node.getAddress(); + if (pc == null || pc0 == null || pc1 == null) { + exit(null); + return; + } + if (pc.compareTo(pc0) < 0 || pc.compareTo(pc1) >= 0) { + TCFSourceRef ref = frame.getLineInfo().getData(); + if (ref != null && ref.area != null) { + if (isSameLine(line_info.area, ref.area)) { + line_info = ref; + ILineNumbers.CodeArea area = line_info.area; + if (area.start_address instanceof BigInteger) pc0 = (BigInteger)area.start_address; + else if (area.start_address != null) pc0 = new BigInteger(area.start_address.toString()); + if (area.end_address instanceof BigInteger) pc1 = (BigInteger)area.end_address; + else if (area.end_address != null) pc1 = new BigInteger(area.end_address.toString()); } else { - set.add(ctx); - node = null; + exit(null); + return; } } } - final Set<IToken> cmds = new HashSet<IToken>(); - for (Iterator<IRunControl.RunControlContext> i = set.iterator(); i.hasNext();) { - IRunControl.RunControlContext ctx = i.next(); - cmds.add(ctx.resume(IRunControl.RM_STEP_INTO, 1, new IRunControl.DoneCommand() { - public void doneCommand(IToken token, Exception error) { - assert cmds.contains(token); - cmds.remove(token); - if (error != null) { - monitor.setStatus(new Status(IStatus.ERROR, - Activator.PLUGIN_ID, IStatus.OK, "Cannot step into", error)); - } - if (cmds.isEmpty()) done(); - } - })); - } } - }; - return true; + step_cnt++; + if (ctx.canResume(IRunControl.RM_STEP_INTO)) { + ctx.resume(IRunControl.RM_STEP_INTO, 1, new IRunControl.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + if (error != null) exit(error); + } + }); + } + else { + exit(new Exception("Step into is not supported")); + } + } + + private boolean isSameLine(ILineNumbers.CodeArea x, ILineNumbers.CodeArea y) { + if (x == null || y == null) return false; + if (x.start_line != y.start_line) return false; + if (x.directory != y.directory && (x.directory == null || !x.directory.equals(y.directory))) return false; + if (x.file != y.file && (x.file == null || !x.file.equals(y.file))) return false; + return true; + } + + private void exit(Throwable error) { + assert started; + if (exited) return; + rc.removeListener(this); + exited = true; + if (error != null) { + monitor.setStatus(new Status(IStatus.ERROR, + Activator.PLUGIN_ID, IStatus.OK, "Cannot step", error)); + } + done.run(); + done(); + } + + public void containerResumed(String[] context_ids) { + } + + public void containerSuspended(String context, String pc, + String reason, Map<String, Object> params, + String[] suspended_ids) { + for (String id : suspended_ids) { + if (!id.equals(context)) contextSuspended(id, null, null, null); + } + contextSuspended(context, pc, reason, params); + } + + public void contextAdded(RunControlContext[] contexts) { + } + + public void contextChanged(RunControlContext[] contexts) { + for (RunControlContext c : contexts) { + if (c.getID().equals(ctx.getID())) ctx = c; + } + } + + public void contextException(String context, String msg) { + if (context.equals(ctx.getID())) exit(new Exception(msg)); + } + + public void contextRemoved(String[] context_ids) { + for (String context : context_ids) { + if (context.equals(ctx.getID())) exit(null); + } + } + + public void contextResumed(String context) { + } + + public void contextSuspended(String context, String pc, String reason, + Map<String, Object> params) { + if (!context.equals(ctx.getID())) return; + if (IRunControl.REASON_STEP.equals(reason)) { + Protocol.invokeLater(this); + } + else { + exit(null); + } + } + } + + public StepIntoCommand(TCFModel model) { + super(model); + } + + @Override + protected boolean canExecute(IRunControl.RunControlContext ctx) { + if (ctx == null) return false; + if (ctx.canResume(IRunControl.RM_STEP_INTO_LINE)) return true; + if (ctx.canResume(IRunControl.RM_STEP_INTO)) return true; + return false; + } + + @Override + protected void execute(final IDebugCommandRequest monitor, final IRunControl.RunControlContext ctx, + boolean src_step, final Runnable done) { + new StepStateMachine(model, monitor, ctx, src_step, done); } } diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/commands/StepOverCommand.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/commands/StepOverCommand.java index 7c22553e9..c71a48a73 100644 --- a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/commands/StepOverCommand.java +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/commands/StepOverCommand.java @@ -10,97 +10,311 @@ *******************************************************************************/ package org.eclipse.tm.internal.tcf.debug.ui.commands; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; +import java.math.BigInteger; +import java.util.HashMap; +import java.util.Map; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.commands.IDebugCommandRequest; -import org.eclipse.debug.core.commands.IEnabledStateRequest; import org.eclipse.debug.core.commands.IStepOverHandler; import org.eclipse.tm.internal.tcf.debug.ui.Activator; import org.eclipse.tm.internal.tcf.debug.ui.model.TCFModel; -import org.eclipse.tm.internal.tcf.debug.ui.model.TCFNode; -import org.eclipse.tm.internal.tcf.debug.ui.model.TCFRunnable; +import org.eclipse.tm.internal.tcf.debug.ui.model.TCFNodeExecContext; +import org.eclipse.tm.internal.tcf.debug.ui.model.TCFNodeStackFrame; +import org.eclipse.tm.internal.tcf.debug.ui.model.TCFSourceRef; import org.eclipse.tm.tcf.protocol.IToken; +import org.eclipse.tm.tcf.protocol.Protocol; +import org.eclipse.tm.tcf.services.IBreakpoints; +import org.eclipse.tm.tcf.services.ILineNumbers; import org.eclipse.tm.tcf.services.IRunControl; +import org.eclipse.tm.tcf.services.IRunControl.RunControlContext; -public class StepOverCommand implements IStepOverHandler { - - private final TCFModel model; - - public StepOverCommand(TCFModel model) { - this.model = model; - } - - public void canExecute(final IEnabledStateRequest monitor) { - new TCFRunnable(model.getDisplay(), monitor) { - public void run() { - Object[] elements = monitor.getElements(); - boolean res = false; - for (int i = 0; i < elements.length; i++) { - TCFNode node = null; - if (elements[i] instanceof TCFNode) node = (TCFNode)elements[i]; - else node = model.getRootNode(); - while (node != null && !node.isDisposed()) { - if (!node.validateNode(this)) return; - IRunControl.RunControlContext ctx = node.getRunContext(); - if (ctx == null || !ctx.canResume(IRunControl.RM_STEP_OVER)) { - node = node.getParent(); - } - else { - if (node.isSuspended()) res = true; - node = null; +public class StepOverCommand extends StepCommand implements IStepOverHandler { + + private static final long TIMEOUT = 10000; + + private static class StepStateMachine extends TCFModel.ContextAction implements IRunControl.RunControlListener { + + private final IDebugCommandRequest monitor; + private final boolean src_step; + private final Runnable done; + private final IRunControl rc = model.getLaunch().getService(IRunControl.class); + private final IBreakpoints bps = model.getLaunch().getService(IBreakpoints.class); + + private IRunControl.RunControlContext ctx; + private TCFNodeExecContext node; + private TCFNodeStackFrame frame; + private TCFSourceRef line_info; + private BigInteger pc0; + private BigInteger pc1; + private int step_cnt; + private Map<String,Object> bp; + private boolean started; + private boolean exited; + + StepStateMachine(TCFModel model, IDebugCommandRequest monitor, + IRunControl.RunControlContext ctx, + boolean src_step, Runnable done) { + super(model, ctx.getID()); + this.monitor = monitor; + this.ctx = ctx; + this.src_step = src_step; + this.done = done; + } + + public void run() { + assert !exited; + if (!started) { + started = true; + rc.addListener(this); + node = (TCFNodeExecContext)model.getNode(ctx.getID()); + if (node == null) { + exit(new Exception("Invalid context ID")); + return; + } + if (!ctx.canResume(src_step ? IRunControl.RM_STEP_OVER_LINE : IRunControl.RM_STEP_OVER)) { + model.invokeLater(TIMEOUT, new Runnable() { + public void run() { + exit(new Exception("Time out")); } + }); + } + } + if (!node.validateNode(this)) return; + if (!node.isSuspended()) { + exit(new Exception("Context is not suspended")); + return; + } + if (ctx.canResume(src_step ? IRunControl.RM_STEP_OVER_LINE : IRunControl.RM_STEP_OVER)) { + if (step_cnt > 0) return; + ctx.resume(src_step ? IRunControl.RM_STEP_OVER_LINE : IRunControl.RM_STEP_OVER, 1, new IRunControl.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + exit(error); } + }); + step_cnt++; + return; + } + if (frame == null) { + frame = node.getTopFrame(); + if (frame == null) { + exit(new Exception("Cannot get top stack frame")); + return; } - monitor.setEnabled(res); - monitor.setStatus(Status.OK_STATUS); - done(); } - }; - } - - public boolean execute(final IDebugCommandRequest monitor) { - new TCFRunnable(model.getDisplay(), monitor) { - public void run() { - Object[] elements = monitor.getElements(); - Set<IRunControl.RunControlContext> set = new HashSet<IRunControl.RunControlContext>(); - for (int i = 0; i < elements.length; i++) { - TCFNode node = null; - if (elements[i] instanceof TCFNode) node = (TCFNode)elements[i]; - else node = model.getRootNode(); - while (node != null && !node.isDisposed()) { - if (!node.validateNode(this)) return; - IRunControl.RunControlContext ctx = node.getRunContext(); - if (ctx == null || !ctx.canResume(IRunControl.RM_STEP_OVER)) { - node = node.getParent(); + if (frame.getFrameNo() >= 0 && !frame.validateNode(this)) return; + if (src_step && line_info == null) { + assert frame.getFrameNo() == 0; + line_info = frame.getLineInfo().getData(); + if (line_info == null) { + exit(new Exception("Line info not available")); + return; + } + if (line_info.error != null) { + exit(line_info.error); + return; + } + ILineNumbers.CodeArea area = line_info.area; + if (area != null) { + if (area.start_address instanceof BigInteger) pc0 = (BigInteger)area.start_address; + else if (area.start_address != null) pc0 = new BigInteger(area.start_address.toString()); + if (area.end_address instanceof BigInteger) pc1 = (BigInteger)area.end_address; + else if (area.end_address != null) pc1 = new BigInteger(area.end_address.toString()); + } + } + if (frame.getFrameNo() != 0) { + if (frame.getFrameNo() < 0) { + // Stepped out of selected function + exit(null); + } + else if (ctx.canResume(IRunControl.RM_STEP_OUT)) { + ctx.resume(IRunControl.RM_STEP_OUT, 1, new IRunControl.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + if (error != null) exit(error); } - else { - set.add(ctx); - node = null; + }); + } + else if (bps != null && ctx.canResume(IRunControl.RM_RESUME)) { + if (bp == null) { + BigInteger addr = frame.getAddress(); + if (addr == null) { + exit(new Exception("Unknown PC address")); + return; } + bp = new HashMap<String,Object>(); + bp.put(IBreakpoints.PROP_ID, "Step" + System.currentTimeMillis()); + bp.put(IBreakpoints.PROP_LOCATION, addr.toString()); + bp.put(IBreakpoints.PROP_CONDITION, "$thread==\"" + ctx.getID() + "\""); + bp.put(IBreakpoints.PROP_ENABLED, Boolean.TRUE); + bps.add(bp, new IBreakpoints.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + if (error != null) exit(error); + } + }); } - } - final Set<IToken> cmds = new HashSet<IToken>(); - for (Iterator<IRunControl.RunControlContext> i = set.iterator(); i.hasNext();) { - IRunControl.RunControlContext ctx = i.next(); - cmds.add(ctx.resume(IRunControl.RM_STEP_OVER, 1, new IRunControl.DoneCommand() { + ctx.resume(IRunControl.RM_RESUME, 1, new IRunControl.DoneCommand() { public void doneCommand(IToken token, Exception error) { - assert cmds.contains(token); - cmds.remove(token); - if (error != null) { - monitor.setStatus(new Status(IStatus.ERROR, - Activator.PLUGIN_ID, IStatus.OK, "Cannot step into", error)); - } - if (cmds.isEmpty()) done(); + if (error != null) exit(error); + } + }); + } + else { + exit(new Exception("Step over is not supported")); + } + return; + } + if (bp != null) { + bps.remove(new String[]{ (String)bp.get(IBreakpoints.PROP_ID) }, new IBreakpoints.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + if (error != null) exit(error); + } + }); + bp = null; + } + if (step_cnt > 0) { + BigInteger pc = node.getAddress(); + if (pc == null || pc0 == null || pc1 == null) { + exit(null); + return; + } + if (pc.compareTo(pc0) < 0 || pc.compareTo(pc1) >= 0) { + TCFSourceRef ref = frame.getLineInfo().getData(); + if (ref != null && ref.area != null) { + if (isSameLine(line_info.area, ref.area)) { + line_info = ref; + ILineNumbers.CodeArea area = line_info.area; + if (area.start_address instanceof BigInteger) pc0 = (BigInteger)area.start_address; + else if (area.start_address != null) pc0 = new BigInteger(area.start_address.toString()); + if (area.end_address instanceof BigInteger) pc1 = (BigInteger)area.end_address; + else if (area.end_address != null) pc1 = new BigInteger(area.end_address.toString()); + } + else { + exit(null); + return; } - })); + } } } - }; - return true; + step_cnt++; + if (ctx.canResume(IRunControl.RM_STEP_OVER)) { + ctx.resume(IRunControl.RM_STEP_OVER, 1, new IRunControl.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + if (error != null) exit(error); + } + }); + } + else if (ctx.canResume(IRunControl.RM_STEP_INTO)) { + ctx.resume(IRunControl.RM_STEP_INTO, 1, new IRunControl.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + if (error != null) exit(error); + } + }); + } + else { + exit(new Exception("Step over is not supported")); + } + } + + private void exit(Throwable error) { + assert started; + if (exited) return; + if (bp != null) { + bps.remove(new String[]{ (String)bp.get(IBreakpoints.PROP_ID) }, new IBreakpoints.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + } + }); + } + rc.removeListener(this); + exited = true; + if (error != null) { + monitor.setStatus(new Status(IStatus.ERROR, + Activator.PLUGIN_ID, IStatus.OK, "Cannot step", error)); + } + done.run(); + done(); + } + + public void containerResumed(String[] context_ids) { + } + + public void containerSuspended(String context, String pc, + String reason, Map<String, Object> params, + String[] suspended_ids) { + for (String id : suspended_ids) { + if (!id.equals(context)) contextSuspended(id, null, null, null); + } + contextSuspended(context, pc, reason, params); + } + + public void contextAdded(RunControlContext[] contexts) { + } + + public void contextChanged(RunControlContext[] contexts) { + for (RunControlContext c : contexts) { + if (c.getID().equals(ctx.getID())) ctx = c; + } + } + + public void contextException(String context, String msg) { + if (context.equals(ctx.getID())) exit(new Exception(msg)); + } + + public void contextRemoved(String[] context_ids) { + for (String context : context_ids) { + if (context.equals(ctx.getID())) exit(null); + } + } + + public void contextResumed(String context) { + } + + public void contextSuspended(String context, String pc, String reason, + Map<String, Object> params) { + if (!context.equals(ctx.getID())) return; + if (IRunControl.REASON_STEP.equals(reason) || isMyBreakpoint(pc, reason)) { + Protocol.invokeLater(this); + } + else { + exit(null); + } + } + + private boolean isSameLine(ILineNumbers.CodeArea x, ILineNumbers.CodeArea y) { + if (x == null || y == null) return false; + if (x.start_line != y.start_line) return false; + if (x.directory != y.directory && (x.directory == null || !x.directory.equals(y.directory))) return false; + if (x.file != y.file && (x.file == null || !x.file.equals(y.file))) return false; + return true; + } + + private boolean isMyBreakpoint(String pc, String reason) { + if (bp == null) return false; + if (pc == null) return false; + if (!IRunControl.REASON_BREAKPOINT.equals(reason)) return false; + BigInteger x = new BigInteger(pc); + BigInteger y = new BigInteger((String)bp.get(IBreakpoints.PROP_LOCATION)); + return x.equals(y); + } + } + + public StepOverCommand(TCFModel model) { + super(model); + } + + @Override + protected boolean canExecute(IRunControl.RunControlContext ctx) { + if (ctx == null) return false; + if (ctx.canResume(IRunControl.RM_STEP_OVER_LINE)) return true; + if (ctx.canResume(IRunControl.RM_STEP_OVER)) return true; + if (ctx.canResume(IRunControl.RM_STEP_INTO) && model.getLaunch().getService(IBreakpoints.class) != null) return true; + return false; + } + + @Override + protected void execute(final IDebugCommandRequest monitor, final IRunControl.RunControlContext ctx, + boolean src_step, final Runnable done) { + new StepStateMachine(model, monitor, ctx, src_step, done); } } diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/commands/StepReturnCommand.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/commands/StepReturnCommand.java index e01a4d26a..45c9aaca6 100644 --- a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/commands/StepReturnCommand.java +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/commands/StepReturnCommand.java @@ -10,97 +10,200 @@ *******************************************************************************/ package org.eclipse.tm.internal.tcf.debug.ui.commands; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; +import java.math.BigInteger; +import java.util.HashMap; +import java.util.Map; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.commands.IDebugCommandRequest; -import org.eclipse.debug.core.commands.IEnabledStateRequest; import org.eclipse.debug.core.commands.IStepReturnHandler; import org.eclipse.tm.internal.tcf.debug.ui.Activator; import org.eclipse.tm.internal.tcf.debug.ui.model.TCFModel; -import org.eclipse.tm.internal.tcf.debug.ui.model.TCFNode; -import org.eclipse.tm.internal.tcf.debug.ui.model.TCFRunnable; +import org.eclipse.tm.internal.tcf.debug.ui.model.TCFNodeExecContext; +import org.eclipse.tm.internal.tcf.debug.ui.model.TCFNodeStackFrame; import org.eclipse.tm.tcf.protocol.IToken; +import org.eclipse.tm.tcf.services.IBreakpoints; import org.eclipse.tm.tcf.services.IRunControl; +import org.eclipse.tm.tcf.services.IRunControl.RunControlContext; -public class StepReturnCommand implements IStepReturnHandler { +public class StepReturnCommand extends StepCommand implements IStepReturnHandler { - private final TCFModel model; - - public StepReturnCommand(TCFModel model) { - this.model = model; - } - - public void canExecute(final IEnabledStateRequest monitor) { - new TCFRunnable(model.getDisplay(), monitor) { - public void run() { - Object[] elements = monitor.getElements(); - boolean res = false; - for (int i = 0; i < elements.length; i++) { - TCFNode node = null; - if (elements[i] instanceof TCFNode) node = (TCFNode)elements[i]; - else node = model.getRootNode(); - while (node != null && !node.isDisposed()) { - if (!node.validateNode(this)) return; - IRunControl.RunControlContext ctx = node.getRunContext(); - if (ctx == null || !ctx.canResume(IRunControl.RM_STEP_OUT)) { - node = node.getParent(); - } - else { - if (node.isSuspended()) res = true; - node = null; + private static final long TIMEOUT = 10000; + + private static class StepStateMachine extends TCFModel.ContextAction implements IRunControl.RunControlListener { + + private final IDebugCommandRequest monitor; + private final Runnable done; + private final IRunControl rc = model.getLaunch().getService(IRunControl.class); + private final IBreakpoints bps = model.getLaunch().getService(IBreakpoints.class); + + private IRunControl.RunControlContext ctx; + private TCFNodeExecContext node; + private TCFNodeStackFrame frame; + private int step_cnt; + private Map<String,Object> bp; + private boolean started; + private boolean exited; + + StepStateMachine(TCFModel model, IDebugCommandRequest monitor, + IRunControl.RunControlContext ctx, Runnable done) { + super(model, ctx.getID()); + this.monitor = monitor; + this.ctx = ctx; + this.done = done; + } + + public void run() { + assert !exited; + if (!started) { + started = true; + rc.addListener(this); + node = (TCFNodeExecContext)model.getNode(ctx.getID()); + if (node == null) { + exit(new Exception("Invalid context ID")); + return; + } + if (!ctx.canResume(IRunControl.RM_STEP_OUT)) { + model.invokeLater(TIMEOUT, new Runnable() { + public void run() { + exit(new Exception("Time out")); } - } + }); } - monitor.setEnabled(res); - monitor.setStatus(Status.OK_STATUS); - done(); } - }; - } - - public boolean execute(final IDebugCommandRequest monitor) { - new TCFRunnable(model.getDisplay(), monitor) { - public void run() { - Object[] elements = monitor.getElements(); - Set<IRunControl.RunControlContext> set = new HashSet<IRunControl.RunControlContext>(); - for (int i = 0; i < elements.length; i++) { - TCFNode node = null; - if (elements[i] instanceof TCFNode) node = (TCFNode)elements[i]; - else node = model.getRootNode(); - while (node != null && !node.isDisposed()) { - if (!node.validateNode(this)) return; - IRunControl.RunControlContext ctx = node.getRunContext(); - if (ctx == null || !ctx.canResume(IRunControl.RM_STEP_OUT)) { - node = node.getParent(); - } - else { - set.add(ctx); - node = null; - } + if (!node.validateNode(this)) return; + if (!node.isSuspended()) { + exit(new Exception("Context is not suspended")); + return; + } + if (ctx.canResume(IRunControl.RM_STEP_OUT)) { + if (step_cnt > 0) return; + ctx.resume(IRunControl.RM_STEP_OUT, 1, new IRunControl.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + exit(error); } + }); + step_cnt++; + return; + } + if (frame == null) { + frame = node.getTopFrame(); + if (frame == null) { + exit(new Exception("Cannot get top stack frame")); + return; } - final Set<IToken> cmds = new HashSet<IToken>(); - for (Iterator<IRunControl.RunControlContext> i = set.iterator(); i.hasNext();) { - IRunControl.RunControlContext ctx = i.next(); - cmds.add(ctx.resume(IRunControl.RM_STEP_OUT, 1, new IRunControl.DoneCommand() { + } + if (!frame.validateNode(this)) return; + if (frame.getFrameNo() < 0) { + // Stepped out of selected function + exit(null); + } + else if (bps != null && ctx.canResume(IRunControl.RM_RESUME)) { + if (bp == null) { + BigInteger addr = frame.getReturnAddress(); + if (addr == null) { + exit(new Exception("Unknown stack frame return address")); + return; + } + bp = new HashMap<String,Object>(); + bp.put(IBreakpoints.PROP_ID, "Step" + System.currentTimeMillis()); + bp.put(IBreakpoints.PROP_LOCATION, addr.toString()); + bp.put(IBreakpoints.PROP_CONDITION, "$thread==\"" + ctx.getID() + "\""); + bp.put(IBreakpoints.PROP_ENABLED, Boolean.TRUE); + bps.add(bp, new IBreakpoints.DoneCommand() { public void doneCommand(IToken token, Exception error) { - assert cmds.contains(token); - cmds.remove(token); - if (error != null) { - monitor.setStatus(new Status(IStatus.ERROR, - Activator.PLUGIN_ID, IStatus.OK, "Cannot step into", error)); - } - if (cmds.isEmpty()) done(); + if (error != null) exit(error); } - })); + }); } + ctx.resume(IRunControl.RM_RESUME, 1, new IRunControl.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + if (error != null) exit(error); + } + }); + } + else { + exit(new Exception("Step out is not supported")); + } + } + + private void exit(Throwable error) { + assert started; + if (exited) return; + if (bp != null) { + bps.remove(new String[]{ (String)bp.get(IBreakpoints.PROP_ID) }, new IBreakpoints.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + } + }); } - }; - return true; + rc.removeListener(this); + exited = true; + if (error != null) { + monitor.setStatus(new Status(IStatus.ERROR, + Activator.PLUGIN_ID, IStatus.OK, "Cannot step", error)); + } + done.run(); + done(); + } + + public void containerResumed(String[] context_ids) { + } + + public void containerSuspended(String context, String pc, + String reason, Map<String, Object> params, + String[] suspended_ids) { + for (String id : suspended_ids) { + if (!id.equals(context)) contextSuspended(id, null, null, null); + } + contextSuspended(context, pc, reason, params); + } + + public void contextAdded(RunControlContext[] contexts) { + } + + public void contextChanged(RunControlContext[] contexts) { + for (RunControlContext c : contexts) { + if (c.getID().equals(ctx.getID())) ctx = c; + } + } + + public void contextException(String context, String msg) { + if (context.equals(ctx.getID())) exit(new Exception(msg)); + } + + public void contextRemoved(String[] context_ids) { + for (String context : context_ids) { + if (context.equals(ctx.getID())) exit(null); + } + } + + public void contextResumed(String context) { + } + + public void contextSuspended(String context, String pc, String reason, + Map<String, Object> params) { + if (!context.equals(ctx.getID())) return; + exit(null); + } + } + + public StepReturnCommand(TCFModel model) { + super(model); + } + + @Override + protected boolean canExecute(IRunControl.RunControlContext ctx) { + if (ctx == null) return false; + if (ctx.canResume(IRunControl.RM_STEP_OUT)) return true; + if (ctx.canResume(IRunControl.RM_RESUME) && model.getLaunch().getService(IBreakpoints.class) != null) return true; + return false; + } + + @Override + protected void execute(final IDebugCommandRequest monitor, final IRunControl.RunControlContext ctx, + boolean src_step, final Runnable done) { + new StepStateMachine(model, monitor, ctx, done); } } diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/launch/TCFSelfTest.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/launch/TCFSelfTest.java index 289a52f13..5c1d74b17 100644 --- a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/launch/TCFSelfTest.java +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/launch/TCFSelfTest.java @@ -717,6 +717,7 @@ class TCFSelfTest { } if (get_state_cmds.isEmpty()) { // No more pending commands + if (active_tests.get(this) == null) return; doneStartingTestProcess(); } } diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFAnnotationManager.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFAnnotationManager.java index 592f06501..4ec3fbded 100644 --- a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFAnnotationManager.java +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFAnnotationManager.java @@ -25,11 +25,9 @@ import org.eclipse.jface.text.Position; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.jface.viewers.ISelection; -import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Event; -import org.eclipse.swt.widgets.Listener; import org.eclipse.tm.internal.tcf.debug.model.ITCFBreakpointListener; import org.eclipse.tm.internal.tcf.debug.model.TCFBreakpoint; import org.eclipse.tm.internal.tcf.debug.model.TCFBreakpointsStatus; @@ -99,7 +97,7 @@ public class TCFAnnotationManager { launch.getBreakpointsStatus().addListener(new ITCFBreakpointListener() { public void breakpointStatusChanged(String id) { - display.asyncExec(new Runnable() { + displayExec(new Runnable() { public void run() { if (active_launch != launch) return; refreshBreakpointView(); @@ -108,7 +106,7 @@ public class TCFAnnotationManager { } public void breakpointRemoved(String id) { - display.asyncExec(new Runnable() { + displayExec(new Runnable() { public void run() { if (active_launch != launch) return; refreshBreakpointView(); @@ -120,19 +118,21 @@ public class TCFAnnotationManager { public void onDisconnected(final TCFLaunch launch) { assert Protocol.isDispatchThread(); - display.asyncExec(new Runnable() { - public void run() { - for (WorkbenchWindowInfo info : windows.values()) { - for (Iterator<TCFAnnotation> i = info.annotations.iterator(); i.hasNext();) { - TCFAnnotation a = i.next(); - if (a.model.getLaunch() == launch) { - i.remove(); - a.dispose(); + synchronized (Device.class) { + displayExec(new Runnable() { + public void run() { + for (WorkbenchWindowInfo info : windows.values()) { + for (Iterator<TCFAnnotation> i = info.annotations.iterator(); i.hasNext();) { + TCFAnnotation a = i.next(); + if (a.model.getLaunch() == launch) { + i.remove(); + a.dispose(); + } } } } - } - }); + }); + } updateActiveLaunch(); } }; @@ -177,47 +177,50 @@ public class TCFAnnotationManager { private boolean disposed; public TCFAnnotationManager() { - assert Thread.currentThread() == display.getThread(); - Protocol.invokeLater(new Runnable() { + assert Protocol.isDispatchThread(); + TCFLaunch.addListener(launch_listener); + displayExec(new Runnable() { public void run() { - TCFLaunch.addListener(launch_listener); - } - }); - for (IWorkbenchWindow window : PlatformUI.getWorkbench().getWorkbenchWindows()) { - window_listener.windowOpened(window); - } - PlatformUI.getWorkbench().addWindowListener(window_listener); - IWorkbenchWindow w = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); - if (w != null) window_listener.windowActivated(w); - display.addListener(SWT.Dispose, new Listener() { - public void handleEvent(Event event) { - dispose(); + for (IWorkbenchWindow window : PlatformUI.getWorkbench().getWorkbenchWindows()) { + window_listener.windowOpened(window); + } + PlatformUI.getWorkbench().addWindowListener(window_listener); + IWorkbenchWindow w = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (w != null) window_listener.windowActivated(w); } }); } public void dispose() { if (disposed) return; - assert Thread.currentThread() == display.getThread(); + assert Protocol.isDispatchThread(); disposed = true; - Protocol.invokeLater(new Runnable() { + TCFLaunch.removeListener(launch_listener); + displayExec(new Runnable() { public void run() { - TCFLaunch.removeListener(launch_listener); + PlatformUI.getWorkbench().removeWindowListener(window_listener); + for (IWorkbenchWindow window : windows.keySet()) { + window.getSelectionService().removeSelectionListener( + IDebugUIConstants.ID_DEBUG_VIEW, selection_listener); + windows.get(window).dispose(); + } + windows.clear(); } }); - PlatformUI.getWorkbench().removeWindowListener(window_listener); - for (IWorkbenchWindow window : windows.keySet()) { - window.getSelectionService().removeSelectionListener( - IDebugUIConstants.ID_DEBUG_VIEW, selection_listener); - windows.get(window).dispose(); + } + + private void displayExec(Runnable r) { + synchronized (Device.class) { + if (!display.isDisposed()) { + display.asyncExec(r); + } } - windows.clear(); } private void updateActiveLaunch() { assert !disposed; final int cnt = ++update_active_launch_cnt; - display.asyncExec(new Runnable() { + displayExec(new Runnable() { public void run() { if (cnt != update_active_launch_cnt) return; TCFLaunch launch = null; @@ -247,7 +250,7 @@ public class TCFAnnotationManager { private void refreshBreakpointView() { assert !disposed; final int cnt = ++refresh_breakpoint_view_cnt; - display.asyncExec(new Runnable() { + displayExec(new Runnable() { public void run() { if (cnt != refresh_breakpoint_view_cnt) return; for (IWorkbenchWindow window : PlatformUI.getWorkbench().getWorkbenchWindows()) { @@ -327,7 +330,21 @@ public class TCFAnnotationManager { } } - void onContextResumed(final TCFModel model, String id) { + public Annotation findAnnotation(TCFModel model, String id) { + if (disposed) return null; + assert Thread.currentThread() == display.getThread(); + for (WorkbenchWindowInfo info : windows.values()) { + for (Iterator<TCFAnnotation> i = info.annotations.iterator(); i.hasNext();) { + TCFAnnotation a = i.next(); + if (a.model == model && a.exe_id.equals(id)) { + return a; + } + } + } + return null; + } + + void onContextResumed(TCFModel model, String id) { if (disposed) return; assert Thread.currentThread() == display.getThread(); for (WorkbenchWindowInfo info : windows.values()) { @@ -350,7 +367,7 @@ public class TCFAnnotationManager { public void run() { IRunControl.RunControlContext x = ((TCFNode)adaptable).getRunContext(); if (x != null && id.equals(x.getID())) { - display.asyncExec(new Runnable() { + displayExec(new Runnable() { public void run() { IWorkbenchWindow w = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); if (w != null) { @@ -365,7 +382,7 @@ public class TCFAnnotationManager { } } - void onContextRemoved(final TCFModel model, String id) { + void onContextRemoved(TCFModel model, String id) { if (disposed) return; assert Thread.currentThread() == display.getThread(); for (WorkbenchWindowInfo info : windows.values()) { 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 8a9b280b6..a9efd4cea 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 @@ -75,6 +75,10 @@ public abstract class TCFChildren extends TCFDataCache<Map<String,TCFNode>> { } } + /** + * Set given data to the cache, mark cache as valid, cancel any pending data retrieval. + * @param data - up-to-date data to store in the cache, null means empty collection of nodes. + */ public void reset(Map<String,TCFNode> data) { if (data != null) { super.reset(data); @@ -85,20 +89,38 @@ public abstract class TCFChildren extends TCFDataCache<Map<String,TCFNode>> { } } + /** + * Add a node to collection of children. + * @param n - a node. + */ void add(TCFNode n) { node_pool.put(n.id, n); if (isValid()) getData().put(n.id, n); } + /** Return collection of all nodes, including current children as well as + * currently unused nodes from the pool. + * To get only current children use getData() method. + * @return Collection of nodes. + */ Collection<TCFNode> getNodes() { return node_pool.values(); } + /** + * Return current number of children. + * The cache must be valid for the method to work. + * @return number of children. + */ int size() { assert isValid(); return getData().size(); } + /** + * Return current children nodes as an array. + * @return array of nodes. + */ TCFNode[] toArray() { assert isValid(); Map<String,TCFNode> data = getData(); 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 e48da9201..17cb419e1 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 @@ -126,10 +126,10 @@ public class TCFChildrenExecContext extends TCFChildren { TCFNodeExecContext n = (TCFNodeExecContext)node.model.getNode(id); if (n == null) { n = new TCFNodeExecContext(node, id); - node.makeModelDelta(IModelDelta.CONTENT); + node.addModelDelta(IModelDelta.CONTENT); } else { - n.makeModelDelta(IModelDelta.STATE); + n.addModelDelta(IModelDelta.STATE); } add(n); run_children.add(n); @@ -142,10 +142,10 @@ public class TCFChildrenExecContext extends TCFChildren { TCFNodeExecContext n = (TCFNodeExecContext)node.model.getNode(id); if (n == null) { n = new TCFNodeExecContext(node, id); - node.makeModelDelta(IModelDelta.CONTENT); + node.addModelDelta(IModelDelta.CONTENT); } else { - n.makeModelDelta(IModelDelta.STATE); + n.addModelDelta(IModelDelta.STATE); } add(n); mem_children.add(n); 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 92998f559..655b90369 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 @@ -22,7 +22,7 @@ public class TCFChildrenRegisters extends TCFChildren { private final TCFNode node; TCFChildrenRegisters(TCFNode node) { - super(node.model.getLaunch().getChannel()); + super(node.model.getLaunch().getChannel(), 32); this.node = node; } 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 071641cfa..3c42c7cb7 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,6 +11,7 @@ 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,12 +20,12 @@ import org.eclipse.tm.tcf.services.IStackTrace; public class TCFChildrenStackTrace extends TCFChildren { private final TCFNodeExecContext node; - private final TCFChildrenRegisters children_regs; + + private String top_frame_id; - TCFChildrenStackTrace(TCFNodeExecContext node, TCFChildrenRegisters children_regs) { + TCFChildrenStackTrace(TCFNodeExecContext node) { super(node.model.getLaunch().getChannel(), 16); this.node = node; - this.children_regs = children_regs; } void onSourceMappingChange() { @@ -43,20 +44,32 @@ public class TCFChildrenStackTrace extends TCFChildren { void onResumed() { reset(null); } + + TCFNodeStackFrame getTopFrame() { + return (TCFNodeStackFrame)node.model.getNode(top_frame_id); + } @Override + public void set(IToken token, Throwable error, Map<String,TCFNode> data) { + for (TCFNode n : getNodes()) { + if (data == null || data.get(n.id) == null) ((TCFNodeStackFrame)n).setFrameNo(-1); + } + super.set(token, error, data); + } + + @Override protected boolean startDataRetrieval() { final HashMap<String,TCFNode> data = new HashMap<String,TCFNode>(); if (!node.isSuspended()) { set(null, null, data); return true; } - String nm = node.id + "-TF"; - TCFNodeStackFrame n = (TCFNodeStackFrame)node.model.getNode(nm); - if (n == null) n = new TCFNodeStackFrame(node, nm, children_regs); - data.put(n.id, n); IStackTrace st = node.model.getLaunch().getService(IStackTrace.class); if (st == null) { + top_frame_id = node.id + "-TF"; + TCFNodeStackFrame n = (TCFNodeStackFrame)node.model.getNode(top_frame_id); + if (n == null) n = new TCFNodeStackFrame(node, top_frame_id); + data.put(n.id, n); set(null, null, data); return true; } @@ -66,17 +79,14 @@ public class TCFChildrenStackTrace extends TCFChildren { if (command == token && error == null) { int cnt = contexts.length; for (String id : contexts) { + cnt--; 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() != 0; + if (n == null) n = new TCFNodeStackFrame(node, id); assert n.id.equals(id); assert n.parent == node; n.setFrameNo(cnt); data.put(id, n); - cnt--; + if (cnt == 0) top_frame_id = id; } } set(token, error, data); 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 6445d105a..8f69833cc 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 @@ -12,6 +12,7 @@ package org.eclipse.tm.internal.tcf.debug.ui.model; import java.util.Collection; import java.util.HashMap; +import java.util.LinkedList; import java.util.Map; import java.util.TreeSet; @@ -38,13 +39,18 @@ 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.TreeModelViewer; +import org.eclipse.debug.ui.AbstractDebugView; import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.debug.ui.IDebugView; import org.eclipse.debug.ui.ISourcePresentation; import org.eclipse.debug.ui.sourcelookup.CommonSourceNotFoundEditorInput; import org.eclipse.debug.ui.sourcelookup.ISourceDisplay; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.custom.BusyIndicator; import org.eclipse.swt.widgets.Display; import org.eclipse.tm.internal.tcf.debug.model.TCFLaunch; @@ -65,7 +71,9 @@ import org.eclipse.tm.tcf.util.TCFDataCache; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.texteditor.ITextEditor; @@ -80,7 +88,7 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, private final Map<String,TCFNode> id2node = new HashMap<String,TCFNode>(); @SuppressWarnings("unchecked") private final Map<Class,Object> commands = new HashMap<Class,Object>(); - private final TreeSet<FutureTask> queue = new TreeSet<FutureTask>(); + private final TreeSet<FutureTask> future_task_queue = new TreeSet<FutureTask>(); private static final Map<ILaunchConfiguration,IEditorInput> editor_not_found = new HashMap<ILaunchConfiguration,IEditorInput>(); @@ -90,7 +98,49 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, private int future_task_cnt; - private int display_source_cnt; + private static int debug_view_selection_cnt; + private static int display_source_cnt; + + private int context_action_cnt; + private final HashMap<String,LinkedList<Runnable>> context_action_queue = + new HashMap<String,LinkedList<Runnable>>(); + + public static abstract class ContextAction implements Runnable { + + protected final TCFModel model; + protected final String context_id; + + public ContextAction(TCFModel model, String context_id) { + assert Protocol.isDispatchThread(); + assert context_id != null; + this.model = model; + this.context_id = context_id; + LinkedList<Runnable> l = model.context_action_queue.get(context_id); + if (l == null) { + l = new LinkedList<Runnable>(); + model.context_action_queue.put(context_id, l); + } + l.add(this); + if (l.getFirst() == this) Protocol.invokeLater(this); + model.context_action_cnt++; + } + + protected void done() { + assert Protocol.isDispatchThread(); + model.context_action_cnt--; + LinkedList<Runnable> l = model.context_action_queue.get(context_id); + if (l == null) return; // context exited + assert l.getFirst() == this; + l.removeFirst(); + if (!l.isEmpty()) { + assert model.context_action_cnt > 0; + Protocol.invokeLater(l.getFirst()); + } + else if (model.context_action_cnt == 0) { + model.fireModelChanged(); + } + } + } private static class FutureTask implements Comparable<FutureTask>{ final int id; @@ -117,19 +167,19 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, private final Thread future_task_dispatcher = new Thread() { public void run() { try { - synchronized (queue) { + synchronized (future_task_queue) { while (!disposed) { - if (queue.isEmpty()) { - queue.wait(); + if (future_task_queue.isEmpty()) { + future_task_queue.wait(); } else { long time = System.currentTimeMillis(); - FutureTask t = queue.first(); + FutureTask t = future_task_queue.first(); if (t.time > time) { - queue.wait(t.time - time); + future_task_queue.wait(t.time - time); } else { - queue.remove(t); + future_task_queue.remove(t); Protocol.invokeLater(t.run); } } @@ -171,13 +221,7 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, } public void contextRemoved(final String[] context_ids) { - for (String id : context_ids) { - TCFNode node = getNode(id); - if (node instanceof TCFNodeExecContext) { - ((TCFNodeExecContext)node).onContextRemoved(); - } - } - fireModelChanged(); + onContextRemoved(context_ids); } public void memoryChanged(String context_id, Number[] addr, long[] size) { @@ -190,7 +234,7 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, }; private final IRunControl.RunControlListener run_listener = new IRunControl.RunControlListener() { - + public void containerResumed(String[] context_ids) { for (int i = 0; i < context_ids.length; i++) { TCFNode node = getNode(context_ids[i]); @@ -251,20 +295,7 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, } public void contextRemoved(final String[] context_ids) { - for (String id : context_ids) { - TCFNode node = getNode(id); - if (node instanceof TCFNodeExecContext) { - ((TCFNodeExecContext)node).onContextRemoved(); - } - } - fireModelChanged(); - display.asyncExec(new Runnable() { - public void run() { - for (String id : context_ids) { - Activator.getAnnotationManager().onContextRemoved(TCFModel.this, id); - } - } - }); + onContextRemoved(context_ids); } public void contextResumed(final String context) { @@ -283,7 +314,9 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, public void contextSuspended(final String context, String pc, String reason, Map<String,Object> params) { TCFNode node = getNode(context); if (node instanceof TCFNodeExecContext) { - ((TCFNodeExecContext)node).onContextSuspended(pc, reason, params); + final TCFNodeExecContext exe = (TCFNodeExecContext)node; + exe.onContextSuspended(pc, reason, params); + setDebugViewSelection(context); } fireModelChanged(); display.asyncExec(new Runnable() { @@ -350,11 +383,12 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, void onDisconnected() { assert Protocol.isDispatchThread(); - TCFNode[] a = id2node.values().toArray(new TCFNode[id2node.size()]); - for (int i = 0; i < a.length; i++) { - if (!a[i].isDisposed()) a[i].dispose(); + if (launch_node != null) { + launchChanged(); + launch_node.dispose(); + launch_node = null; } - launchChanged(); + assert id2node.size() == 0; } void onProxyInstalled(final TCFModelProxy p) { @@ -372,6 +406,38 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, } }); } + + private void onContextRemoved(final String[] context_ids) { + boolean close_channel = false; + for (String id : context_ids) { + context_action_queue.remove(id); + TCFNode node = getNode(id); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onContextRemoved(); + if (node.parent == launch_node) close_channel = true; + } + } + fireModelChanged(); + if (close_channel) { + // Close end debug session if the last context is removed: + Protocol.invokeLater(new Runnable() { + public void run() { + if (launch_node == null) return; + if (launch_node.isDisposed()) return; + if (!launch_node.validateNode(this)) return; + if (launch_node.getContextCount() != 0) return; + launch.onLastContextRemoved(); + } + }); + } + display.asyncExec(new Runnable() { + public void run() { + for (String id : context_ids) { + Activator.getAnnotationManager().onContextRemoved(TCFModel.this, id); + } + } + }); + } Collection<TCFModelProxy> getModelProxyList() { return model_proxies.values(); @@ -379,15 +445,15 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, void launchChanged() { if (launch_node != null) { - launch_node.makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); + launch_node.addModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); fireModelChanged(); } } void dispose() { - synchronized (queue) { + synchronized (future_task_queue) { disposed = true; - queue.notify(); + future_task_queue.notify(); } try { future_task_dispatcher.join(); @@ -413,6 +479,7 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, void fireModelChanged() { assert Protocol.isDispatchThread(); + if (context_action_cnt > 0) return; for (TCFModelProxy p : model_proxies.values()) p.fireModelChanged(); } @@ -436,12 +503,12 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, } public void invokeLater(long delay, Runnable run) { - synchronized (queue) { - queue.add(new FutureTask(future_task_cnt++, System.currentTimeMillis() + delay, run)); - queue.notify(); + synchronized (future_task_queue) { + future_task_queue.add(new FutureTask(future_task_cnt++, System.currentTimeMillis() + delay, run)); + future_task_queue.notify(); } } - + public void update(IChildrenCountUpdate[] updates) { for (int i = 0; i < updates.length; i++) { Object o = updates[i].getElement(); @@ -498,6 +565,7 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, public void update(ILabelUpdate[] updates) { for (int i = 0; i < updates.length; i++) { Object o = updates[i].getElement(); + // Launch label is provided by TCFLaunchLabelProvider class. assert !(o instanceof TCFLaunch); ((TCFNode)o).update(updates[i]); } @@ -521,6 +589,50 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, return null; } + public void setDebugViewSelection(final String node_id) { + final int cnt = ++debug_view_selection_cnt; + display.asyncExec(new Runnable() { + public void run() { + if (cnt != debug_view_selection_cnt) return; + final IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (window == null) return; + final IDebugView view = (IDebugView)window.getActivePage().findView(IDebugUIConstants.ID_DEBUG_VIEW); + if (view == null) return; + if (!((AbstractDebugView)view).isAvailable()) return; + invokeLater(300, new Runnable() { + public void run() { + TCFNode node = getNode(node_id); + if (node == null) return; + if (node.disposed) return; + if (!node.validateNode(this)) return; + if (node instanceof TCFNodeExecContext) { + TCFNode frame = ((TCFNodeExecContext)node).getTopFrame(); + if (frame != null && !frame.disposed) { + if (!frame.validateNode(this)) return; + node = frame; + } + } + final TreeModelViewer viewer = (TreeModelViewer)view.getViewer(); + for (TCFModelProxy proxy : model_proxies.values()) { + if (proxy.getProxyViewer() == viewer && !proxy.validateViewer(this)) return; + } + final TCFNode element = node; + display.asyncExec(new Runnable() { + public void run() { + if (cnt != debug_view_selection_cnt) return; + if (element.parent != null && !viewer.getExpandedState(element.parent)) { + viewer.setExpandedState(element.parent, true); + } + ISelection selection = new StructuredSelection(element); + viewer.setSelection(selection); + } + }); + } + }); + } + }); + } + /** * Reveal source code associated with given model element. */ 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 7a4960b90..2491fa900 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,7 +19,6 @@ 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; @@ -44,29 +43,38 @@ public class TCFModelManager { private final ILaunchesListener debug_launch_listener = new ILaunchesListener() { public void launchesAdded(final ILaunch[] launches) { - } - - public void launchesChanged(final ILaunch[] launches) { Protocol.invokeAndWait(new Runnable() { public void run() { for (int i = 0; i < launches.length; i++) { if (launches[i] instanceof TCFLaunch) { - TCFModel model = models.get(launches[i]); - if (model != null) model.launchChanged(); + TCFLaunch launch = (TCFLaunch)launches[i]; + assert models.get(launch) == null; + TCFModel model = new TCFModel(display, launch); + models.put(launch, model); + assert launch.getChannel() == null; } } } }); } + public void launchesChanged(final ILaunch[] launches) { + Protocol.invokeAndWait(new Runnable() { + public void run() { + for (ILaunch launch : launches) { + TCFModel model = models.get(launch); + if (model != null) model.launchChanged(); + } + } + }); + } + public void launchesRemoved(final ILaunch[] launches) { Protocol.invokeAndWait(new Runnable() { public void run() { - for (int i = 0; i < launches.length; i++) { - if (launches[i] instanceof TCFLaunch) { - TCFModel model = models.remove(launches[i]); - if (model != null) model.dispose(); - } + for (ILaunch launch : launches) { + TCFModel model = models.remove(launch); + if (model != null) model.dispose(); } } }); @@ -74,47 +82,32 @@ public class TCFModelManager { }; public TCFModelManager() { + assert Protocol.isDispatchThread(); display = Display.getDefault(); DebugPlugin.getDefault().getLaunchManager().addLaunchListener(debug_launch_listener); - Protocol.invokeAndWait(new Runnable() { - public void run() { - TCFLaunch.addListener(tcf_launch_listener); - } - }); + TCFLaunch.addListener(tcf_launch_listener); } public void dispose() { + assert Protocol.isDispatchThread(); DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(debug_launch_listener); - Protocol.invokeAndWait(new Runnable() { - public void run() { - TCFLaunch.removeListener(tcf_launch_listener); - for (Iterator<TCFModel> i = models.values().iterator(); i.hasNext();) { - TCFModel model = i.next(); - model.dispose(); - i.remove(); - } - assert models.isEmpty(); - } - }); + TCFLaunch.removeListener(tcf_launch_listener); + for (Iterator<TCFModel> i = models.values().iterator(); i.hasNext();) { + TCFModel model = i.next(); + model.dispose(); + i.remove(); + } + assert models.isEmpty(); } public TCFModel getModel(TCFLaunch launch) { assert Protocol.isDispatchThread(); - TCFModel model = models.get(launch); - if (model == null) { - model = new TCFModel(display, launch); - models.put(launch, model); - if (!launch.isConnecting()) { - IChannel channel = launch.getChannel(); - if (channel != null && channel.getState() == IChannel.STATE_OPEN) { - tcf_launch_listener.onConnected(launch); - } - } - } - return model; + return models.get(launch); } public TCFNode getRootNode(TCFLaunch launch) { - return getModel(launch).getRootNode(); + TCFModel model = getModel(launch); + if (model == null) return null; + return model.getRootNode(); } } 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 ac5694bc1..3c2a19199 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,6 +10,7 @@ *******************************************************************************/ package org.eclipse.tm.internal.tcf.debug.ui.model; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -17,14 +18,47 @@ 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.IViewerUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener; import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer; 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>(); + private final Map<TCFNode,Integer> deltas = new HashMap<TCFNode,Integer>(); + private final ArrayList<Runnable> wait_list = new ArrayList<Runnable>(); + + private boolean pending_deltas; + private boolean updating; + + private final IViewerUpdateListener update_listener = new IViewerUpdateListener() { + public void viewerUpdatesComplete() { + Protocol.invokeLater(new Runnable() { + public void run() { + pending_deltas = false; + updating = false; + if (wait_list.isEmpty()) return; + Runnable[] arr = wait_list.toArray(new Runnable[wait_list.size()]); + for (Runnable r : arr) r.run(); + wait_list.clear(); + } + }); + } + public void viewerUpdatesBegin() { + Protocol.invokeLater(new Runnable() { + public void run() { + updating = true; + } + }); + } + public void updateStarted(IViewerUpdate update) { + } + public void updateComplete(IViewerUpdate update) { + } + }; TCFModelProxy(TCFModel model) { this.model = model; @@ -33,33 +67,68 @@ public class TCFModelProxy extends AbstractModelProxy implements IModelProxy { public void installed(Viewer viewer) { super.installed(viewer); model.onProxyInstalled(this); + ((TreeModelViewer)viewer).addViewerUpdateListener(update_listener); } public void dispose() { + ((TreeModelViewer)getViewer()).removeViewerUpdateListener(update_listener); model.onProxyDisposed(this); super.dispose(); } - ModelDelta getDelta(TCFNode node) { - return deltas.get(node); + void addDelta(TCFNode node, int flags) { + Integer delta = deltas.get(node); + if (delta != null) { + deltas.put(node, Integer.valueOf(delta.intValue() | flags)); + } + else { + deltas.put(node, Integer.valueOf(flags)); + } } - - 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); + + Viewer getProxyViewer() { + return getViewer(); + } + + boolean validateViewer(Runnable done) { + assert Protocol.isDispatchThread(); + if (pending_deltas || updating) { + wait_list.add(done); + return false; + } + return true; + } + + private ModelDelta makeDelta(ModelDelta root, Map<TCFNode,ModelDelta> map, TCFNode node, int flags) { + ModelDelta delta = map.get(node); + if (delta == null) { + if (node.parent == null) { + delta = root.addNode(model.getLaunch(), flags); + map.put(node, delta); + } + else { + delta = makeDelta(root, map, node.parent, 0).addNode(node, flags); + map.put(node, delta); + } + } + else if (flags != 0) { + delta.setFlags(delta.getFlags() | flags); + } + return 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); + if (deltas.isEmpty()) return; + ModelDelta root = new ModelDelta(DebugPlugin.getDefault().getLaunchManager(), IModelDelta.NO_CHANGE); + Map<TCFNode,ModelDelta> map = new HashMap<TCFNode,ModelDelta>(); + for (TCFNode node : deltas.keySet()) { + if (node.disposed) continue; + makeDelta(root, map, node, deltas.get(node).intValue()); } + deltas.clear(); + if (map.isEmpty()) return; + fireModelChanged(root); + pending_deltas = true; } } 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 10afd3ee6..e9125e834 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,6 +10,7 @@ *******************************************************************************/ package org.eclipse.tm.internal.tcf.debug.ui.model; +import java.math.BigInteger; import java.util.Collection; import org.eclipse.core.runtime.PlatformObject; @@ -25,7 +26,6 @@ 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.debug.ui.sourcelookup.ISourceDisplay; import org.eclipse.tm.internal.tcf.debug.ui.ImageCache; import org.eclipse.tm.tcf.protocol.IChannel; @@ -62,6 +62,8 @@ public abstract class TCFNode extends PlatformObject implements Comparable<TCFNo */ protected TCFNode(TCFNode parent, String id) { assert Protocol.isDispatchThread(); + assert parent != null; + assert id != null; this.parent = parent; this.id = id; model = parent.model; @@ -76,7 +78,7 @@ public abstract class TCFNode extends PlatformObject implements Comparable<TCFNo assert !disposed; if (parent != null) parent.dispose(id); invalidateNode(); - model.removeNode(id); + if (id != null) model.removeNode(id); disposed = true; } @@ -137,7 +139,7 @@ public abstract class TCFNode extends PlatformObject implements Comparable<TCFNo * For executable contexts and stack frames address is current PC. * @return */ - public String getAddress() { + public BigInteger getAddress() { return null; } @@ -152,16 +154,17 @@ public abstract class TCFNode extends PlatformObject implements Comparable<TCFNo final void update(final IChildrenCountUpdate result) { new TCFRunnable(model.getDisplay(), result) { public void run() { - if (result.isCanceled()) return; - IChannel channel = model.getLaunch().getChannel(); - if (!disposed && channel.getState() == IChannel.STATE_OPEN) { - if (!validateNode(this)) return; - getData(result); + if (!result.isCanceled()) { + IChannel channel = model.getLaunch().getChannel(); + if (!disposed && channel.getState() == IChannel.STATE_OPEN) { + if (!validateNode(this)) return; + getData(result); + } + else { + result.setChildCount(0); + } + result.setStatus(Status.OK_STATUS); } - else { - result.setChildCount(0); - } - result.setStatus(Status.OK_STATUS); done(); } }; @@ -174,13 +177,14 @@ public abstract class TCFNode extends PlatformObject implements Comparable<TCFNo final void update(final IChildrenUpdate result) { new TCFRunnable(model.getDisplay(), result) { public void run() { - if (result.isCanceled()) return; - IChannel channel = model.getLaunch().getChannel(); - if (!disposed && channel.getState() == IChannel.STATE_OPEN) { - if (!validateNode(this)) return; - getData(result); + if (!result.isCanceled()) { + IChannel channel = model.getLaunch().getChannel(); + if (!disposed && channel.getState() == IChannel.STATE_OPEN) { + if (!validateNode(this)) return; + getData(result); + } + result.setStatus(Status.OK_STATUS); } - result.setStatus(Status.OK_STATUS); done(); } }; @@ -193,16 +197,17 @@ public abstract class TCFNode extends PlatformObject implements Comparable<TCFNo final void update(final IHasChildrenUpdate result) { new TCFRunnable(model.getDisplay(), result) { public void run() { - if (result.isCanceled()) return; - IChannel channel = model.getLaunch().getChannel(); - if (!disposed && channel.getState() == IChannel.STATE_OPEN) { - if (!validateNode(this)) return; - getData(result); - } - else { - result.setHasChilren(false); + if (!result.isCanceled()) { + IChannel channel = model.getLaunch().getChannel(); + if (!disposed && channel.getState() == IChannel.STATE_OPEN) { + if (!validateNode(this)) return; + getData(result); + } + else { + result.setHasChilren(false); + } + result.setStatus(Status.OK_STATUS); } - result.setStatus(Status.OK_STATUS); done(); } }; @@ -215,16 +220,17 @@ public abstract class TCFNode extends PlatformObject implements Comparable<TCFNo final void update(final ILabelUpdate result) { new TCFRunnable(model.getDisplay(), result) { public void run() { - if (result.isCanceled()) return; - IChannel channel = model.getLaunch().getChannel(); - if (!disposed && channel.getState() == IChannel.STATE_OPEN) { - if (!validateNode(this)) return; - getData(result); - } - else { - result.setLabel("...", 0); + if (!result.isCanceled()) { + IChannel channel = model.getLaunch().getChannel(); + if (!disposed && channel.getState() == IChannel.STATE_OPEN) { + if (!validateNode(this)) return; + getData(result); + } + else { + result.setLabel("...", 0); + } + result.setStatus(Status.OK_STATUS); } - result.setStatus(Status.OK_STATUS); done(); } }; @@ -278,10 +284,10 @@ public abstract class TCFNode extends PlatformObject implements Comparable<TCFNo * 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) { + final void addModelDelta(int flags) { for (TCFModelProxy p : model.getModelProxyList()) { int f = flags & getRelevantModelDeltaFlags(p.getPresentationContext()); - if (f != 0) makeModelDelta(p, f); + if (f != 0) p.addDelta(this, f); } } @@ -295,25 +301,6 @@ public abstract class TCFNode extends PlatformObject implements Comparable<TCFNo 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(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); - } - return delta; - } - /*--------------------------------------------------------------------------------------*/ /* Node data retrieval state machine */ 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 d7c3a2e93..8ec20b19e 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 @@ -56,8 +56,8 @@ public class TCFNodeExecContext extends TCFNode { TCFNodeExecContext(TCFNode parent, final String id) { super(parent, id); children_exec = new TCFChildrenExecContext(this); + children_stack = new TCFChildrenStackTrace(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) { @@ -190,7 +190,7 @@ public class TCFNodeExecContext extends TCFNode { } @Override - public String getAddress() { + public BigInteger getAddress() { assert Protocol.isDispatchThread(); if (!run_context.isValid()) return null; IRunControl.RunControlContext ctx = run_context.getData(); @@ -198,7 +198,14 @@ public class TCFNodeExecContext extends TCFNode { if (!state.isValid()) return null; ContextState s = state.getData(); if (s == null) return null; - return s.suspended_pc; + if (s.suspended_pc == null) return null; + return new BigInteger(s.suspended_pc); + } + + public TCFNodeStackFrame getTopFrame() { + assert Protocol.isDispatchThread(); + if (!children_stack.isValid()) return null; + return children_stack.getTopFrame(); } @Override @@ -300,7 +307,7 @@ public class TCFNodeExecContext extends TCFNode { run_context.reset(context); resumed_cnt++; children_stack.onSourceMappingChange(); - makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); + addModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); } void onContextAdded(IMemory.MemoryContext context) { @@ -311,14 +318,14 @@ public class TCFNodeExecContext extends TCFNode { assert !disposed; mem_context.reset(context); resumed_cnt++; - makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); + addModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); } void onContextRemoved() { assert !disposed; resumed_cnt++; dispose(); - parent.makeModelDelta(IModelDelta.CONTENT); + parent.addModelDelta(IModelDelta.CONTENT); } void onContainerSuspended() { @@ -330,7 +337,8 @@ public class TCFNodeExecContext extends TCFNode { } state.reset(); children_stack.onSuspended(); - makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); + children_regs.onSuspended(); + addModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); } void onContainerResumed() { @@ -341,7 +349,7 @@ public class TCFNodeExecContext extends TCFNode { if (!ctx.hasState()) return; } state.reset(); - makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); + addModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); } void onContextSuspended(String pc, String reason, Map<String,Object> params) { @@ -352,14 +360,15 @@ public class TCFNodeExecContext extends TCFNode { s.suspended_reason = reason; state.reset(s); children_stack.onSuspended(); + children_regs.onSuspended(); resumed_cnt++; - makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); + addModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); } void onContextResumed() { assert !disposed; state.reset(new ContextState()); - makeModelDelta(IModelDelta.STATE); + addModelDelta(IModelDelta.STATE); final int cnt = ++resumed_cnt; model.invokeLater(250, new Runnable() { public void run() { @@ -367,7 +376,7 @@ public class TCFNodeExecContext extends TCFNode { if (disposed) return; children_stack.onResumed(); if (!validateNode(this)) return; - makeModelDelta(IModelDelta.CONTENT); + addModelDelta(IModelDelta.CONTENT); if (parent instanceof TCFNodeExecContext) { ((TCFNodeExecContext)parent).onChildResumedOrSuspended(); } @@ -378,7 +387,7 @@ public class TCFNodeExecContext extends TCFNode { void onChildResumedOrSuspended() { IRunControl.RunControlContext ctx = run_context.getData(); - if (ctx != null && ctx.isContainer()) makeModelDelta(IModelDelta.STATE); + if (ctx != null && ctx.isContainer()) addModelDelta(IModelDelta.STATE); if (parent instanceof TCFNodeExecContext) ((TCFNodeExecContext)parent).onChildResumedOrSuspended(); } @@ -391,7 +400,8 @@ public class TCFNodeExecContext extends TCFNode { void onRegistersChanged() { children_stack.onRegistersChanged(); - makeModelDelta(IModelDelta.CONTENT); + children_regs.onRegistersChanged(); + addModelDelta(IModelDelta.CONTENT); } @Override @@ -427,8 +437,8 @@ public class TCFNodeExecContext extends TCFNode { children_exec.wait(done); return false; } - children_regs.validate(); children_stack.validate(); + children_regs.validate(); IRunControl.RunControlContext ctx = run_context.getData(); if (ctx != null && !ctx.hasState()) { @@ -441,14 +451,15 @@ public class TCFNodeExecContext extends TCFNode { } } - if (!children_regs.isValid()) { - children_regs.wait(done); - return false; - } if (!children_stack.isValid()) { children_stack.wait(done); return false; } + if (!children_regs.isValid()) { + children_regs.wait(done); + return false; + } + return true; } 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 84cf9df0b..6afc906d0 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 @@ -12,12 +12,9 @@ package org.eclipse.tm.internal.tcf.debug.ui.model; import java.util.Arrays; -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.IModelDelta; -import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; import org.eclipse.tm.tcf.services.IMemory; import org.eclipse.tm.tcf.services.IRunControl; @@ -74,19 +71,10 @@ public class TCFNodeLaunch extends TCFNode { void onContextAdded(IMemory.MemoryContext context) { children.onContextAdded(context); } - - @Override - 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, -1); - p.addDelta(this, delta); - } - else { - delta.setFlags(delta.getFlags() | flags); - } - return delta; + + int getContextCount() { + assert children.isValid(); + return children.size(); } @Override 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 e9b8cbc66..9b98d772b 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 @@ -200,13 +200,13 @@ public class TCFNodeRegister extends TCFNode { */ void onSuspended() { value.reset(); - makeModelDelta(IModelDelta.STATE); + addModelDelta(IModelDelta.STATE); } void onRegistersChanged() { context.reset(); value.reset(); - makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); + addModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); } @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 91ba67499..141b8ad4d 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 @@ -39,21 +39,24 @@ public class TCFNodeStackFrame extends TCFNode { 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) { + TCFNodeStackFrame(final TCFNodeExecContext parent, final String id) { super(parent, id); - this.frame_no = frame_no; - if (regs == null) regs = new TCFChildrenRegisters(this); - this.children_regs = regs; + children_regs = new TCFChildrenRegisters(this); IChannel channel = model.getLaunch().getChannel(); stack_trace_context = new TCFDataCache<IStackTrace.StackTraceContext>(channel) { @Override protected boolean startDataRetrieval() { assert command == null; - if (frame_no == 0) { + if (!isSuspended()) { set(null, null, null); return true; } IStackTrace st = model.getLaunch().getService(IStackTrace.class); + if (st == null) { + assert frame_no == 0; + set(null, null, null); + return true; + } command = st.getContext(new String[]{ id }, new IStackTrace.DoneGetContext() { public void doneGetContext(IToken token, Exception error, IStackTrace.StackTraceContext[] context) { set(token, error, context == null || context.length == 0 ? null : context[0]); @@ -66,18 +69,11 @@ public class TCFNodeStackFrame extends TCFNode { 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; - } + if (!stack_trace_context.validate()) { + stack_trace_context.wait(this); + return false; } - String s = getAddress(); - if (s != null) n = new BigInteger(s); + BigInteger n = getAddress(); if (n == null) { set(null, null, null); return true; @@ -117,37 +113,28 @@ public class TCFNodeStackFrame extends TCFNode { }; } - 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() { + public int getFrameNo() { assert Protocol.isDispatchThread(); return frame_no; } void setFrameNo(int frame_no) { - assert this.frame_no != 0 && frame_no != 0; this.frame_no = frame_no; } - TCFDataCache<TCFSourceRef> getLineInfo() { + public TCFDataCache<TCFSourceRef> getLineInfo() { return line_info; } @Override void dispose() { - if (frame_no != 0) children_regs.dispose(); + children_regs.dispose(); super.dispose(); } @Override void dispose(String id) { - if (frame_no != 0) children_regs.dispose(id); + children_regs.dispose(id); } @Override @@ -171,14 +158,27 @@ public class TCFNodeStackFrame extends TCFNode { } @Override - public String getAddress() { + public BigInteger getAddress() { assert Protocol.isDispatchThread(); + if (!stack_trace_context.isValid()) return null; + IStackTrace.StackTraceContext ctx = stack_trace_context.getData(); + if (ctx != null) { + Number n = ctx.getInstructionAddress(); + if (n instanceof BigInteger) return (BigInteger)n; + if (n != null) return new BigInteger(n.toString()); + } if (frame_no == 0) return parent.getAddress(); + return null; + } + + public BigInteger getReturnAddress() { + assert Protocol.isDispatchThread(); 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(); + Number n = ctx.getReturnAddress(); + if (n instanceof BigInteger) return (BigInteger)n; + if (n != null) return new BigInteger(n.toString()); } return null; } @@ -186,6 +186,10 @@ public class TCFNodeStackFrame extends TCFNode { @Override protected void getData(IChildrenCountUpdate result) { if (IDebugUIConstants.ID_REGISTER_VIEW.equals(result.getPresentationContext().getId())) { + if (frame_no == 0) { + parent.getData(result); + return; + } result.setChildCount(children_regs.size()); } else { @@ -197,6 +201,10 @@ public class TCFNodeStackFrame extends TCFNode { protected void getData(IChildrenUpdate result) { TCFNode[] arr = null; if (IDebugUIConstants.ID_REGISTER_VIEW.equals(result.getPresentationContext().getId())) { + if (frame_no == 0) { + parent.getData(result); + return; + } arr = children_regs.toArray(); } else { @@ -219,6 +227,10 @@ public class TCFNodeStackFrame extends TCFNode { @Override protected void getData(IHasChildrenUpdate result) { if (IDebugUIConstants.ID_REGISTER_VIEW.equals(result.getPresentationContext().getId())) { + if (frame_no == 0) { + parent.getData(result); + return; + } result.setHasChilren(children_regs.size() > 0); } else { @@ -231,7 +243,7 @@ public class TCFNodeStackFrame extends TCFNode { result.setImageDescriptor(ImageCache.getImageDescriptor(getImageName()), 0); Throwable error = stack_trace_context.getError(); if (error == null) error = line_info.getError(); - if (error != null) { + if (error != null && isSuspended()) { result.setForeground(new RGB(255, 0, 0), 0); result.setLabel(error.getClass().getName() + ": " + error.getMessage(), 0); } @@ -265,19 +277,19 @@ public class TCFNodeStackFrame extends TCFNode { void onSourceMappingChange() { line_info.reset(); - makeModelDelta(IModelDelta.STATE); + addModelDelta(IModelDelta.STATE); } void onSuspended() { stack_trace_context.reset(); line_info.reset(); children_regs.onSuspended(); - makeModelDelta(IModelDelta.STATE); + addModelDelta(IModelDelta.STATE); } void onRegistersChanged() { children_regs.onRegistersChanged(); - makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); + addModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); } @Override @@ -291,12 +303,12 @@ public class TCFNodeStackFrame extends TCFNode { public boolean validateNode(Runnable done) { if (frame_no == 0 && !parent.validateNode(done)) return false; stack_trace_context.validate(); - children_regs.validate(); + if (frame_no != 0) children_regs.validate(); if (!stack_trace_context.isValid()) { stack_trace_context.wait(done); return false; } - if (!children_regs.isValid()) { + if (frame_no != 0 && !children_regs.isValid()) { children_regs.wait(done); return false; } diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFRunnable.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFRunnable.java index b9e387a92..cbfbba5fd 100644 --- a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFRunnable.java +++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/model/TCFRunnable.java @@ -20,8 +20,6 @@ public abstract class TCFRunnable implements Runnable { private final IRequest monitor; private final Display display; - private boolean canceled; - public TCFRunnable() { monitor = null; display = null; @@ -33,17 +31,6 @@ public abstract class TCFRunnable implements Runnable { Protocol.invokeLater(this); } - public void cancel() { - canceled = true; - if (display == null) return; - display.asyncExec(new Runnable() { - public void run() { - monitor.cancel(); - monitor.done(); - } - }); - } - public void done() { if (display == null) return; display.asyncExec(new Runnable() { @@ -52,8 +39,4 @@ public abstract class TCFRunnable implements Runnable { } }); } - - public boolean isCanceled() { - return canceled; - } } 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 index e8e213406..19cbdf74d 100644 --- 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 @@ -17,8 +17,8 @@ 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; +public class TCFSourceRef { + public BigInteger address; + public ILineNumbers.CodeArea area; + public Throwable error; } diff --git a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/model/TCFLaunch.java b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/model/TCFLaunch.java index 3fd7701c3..d3c290c19 100644 --- a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/model/TCFLaunch.java +++ b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/model/TCFLaunch.java @@ -62,6 +62,7 @@ public class TCFLaunch extends Launch { private boolean connecting; private boolean disconnected; private boolean shutdown; + private boolean last_context_exited; private ProcessContext process; public TCFLaunch(ILaunchConfiguration launchConfiguration, String mode) { @@ -238,6 +239,11 @@ public class TCFLaunch extends Launch { public boolean isConnecting() { return connecting; } + + public void onLastContextRemoved() { + last_context_exited = true; + channel.close(); + } public IPeer getPeer() { assert Protocol.isDispatchThread(); @@ -310,6 +316,10 @@ public class TCFLaunch extends Launch { return res[0]; } + public boolean isExited() { + return last_context_exited; + } + public void disconnect() throws DebugException { Protocol.invokeAndWait(new Runnable() { public void run() { diff --git a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/local/LocatorService.java b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/local/LocatorService.java index 75c0117fa..20e263a6d 100644 --- a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/local/LocatorService.java +++ b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/local/LocatorService.java @@ -27,6 +27,7 @@ import org.eclipse.tm.internal.tcf.core.RemotePeer; import org.eclipse.tm.tcf.Activator; import org.eclipse.tm.tcf.core.AbstractChannel; import org.eclipse.tm.tcf.protocol.IChannel; +import org.eclipse.tm.tcf.protocol.IEventQueue; import org.eclipse.tm.tcf.protocol.IPeer; import org.eclipse.tm.tcf.protocol.IToken; import org.eclipse.tm.tcf.protocol.JSON; @@ -51,15 +52,18 @@ public class LocatorService implements ILocator { private Thread output_thread = new Thread() { public void run() { - for (;;) { - Protocol.invokeAndWait(new Runnable() { - public void run() { - sendPeerInfoRequest(); - } - }); + while (true) { try { + Protocol.invokeAndWait(new Runnable() { + public void run() { + sendPeerInfoRequest(); + } + }); sleep(5 * 1000); } + catch (IllegalStateException x) { + break; + } catch (InterruptedException x) { break; } diff --git a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/remote/StackTraceProxy.java b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/remote/StackTraceProxy.java index e59aa731f..994bc6699 100644 --- a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/remote/StackTraceProxy.java +++ b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/remote/StackTraceProxy.java @@ -63,6 +63,10 @@ public class StackTraceProxy implements IStackTrace { return (Number)props.get(PROP_RETURN_ADDRESS); } + public Number getInstructionAddress() { + return (Number)props.get(PROP_INSTRUCTION_ADDRESS); + } + public Map<String, Object> getProperties() { return props; } diff --git a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/Activator.java b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/Activator.java index d1df1ce56..69ddd599f 100644 --- a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/Activator.java +++ b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/Activator.java @@ -20,6 +20,8 @@ import org.eclipse.core.runtime.Status; import org.eclipse.tm.tcf.protocol.Protocol; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleEvent; +import org.osgi.framework.BundleListener; /** @@ -27,11 +29,20 @@ import org.osgi.framework.BundleContext; */ public class Activator extends Plugin { - // The plug-in ID public static final String PLUGIN_ID = "org.eclipse.tm.tcf"; - // The shared instance private static Activator plugin; + private static final EventQueue queue = new EventQueue(); + private static final BundleListener bundle_listener = new BundleListener() { + private boolean started = false; + public void bundleChanged(BundleEvent event) { + if (plugin != null && !started && event.getBundle() == plugin.getBundle() && + plugin.getBundle().getState() == Bundle.ACTIVE) { + queue.start(); + started = true; + } + } + }; public Activator() { plugin = this; @@ -40,14 +51,19 @@ public class Activator extends Plugin { @Override public void start(BundleContext context) throws Exception { super.start(context); - EventQueue e = new EventQueue(); - Protocol.setEventQueue(e); - runTCFStartup(); - e.start(); + Protocol.setEventQueue(queue); + Protocol.invokeLater(new Runnable() { + public void run() { + runTCFStartup(); + } + }); + context.addBundleListener(bundle_listener); } @Override public void stop(BundleContext context) throws Exception { + context.removeBundleListener(bundle_listener); + queue.shutdown(); plugin = null; super.stop(context); } diff --git a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/EventQueue.java b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/EventQueue.java index 61bb453cd..df0a7c679 100644 --- a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/EventQueue.java +++ b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/EventQueue.java @@ -29,6 +29,7 @@ class EventQueue implements IEventQueue, Runnable { private final LinkedList<Runnable> queue = new LinkedList<Runnable>(); private final Thread thread; private boolean waiting; + private boolean shutdown; private int job_cnt; EventQueue() { @@ -64,6 +65,22 @@ class EventQueue implements IEventQueue, Runnable { void start() { thread.start(); } + + void shutdown() { + try { + synchronized (this) { + shutdown = true; + if (waiting) { + waiting = false; + notifyAll(); + } + } + thread.join(); + } + catch (Exception e) { + Activator.log("Failed to shutdown TCF event dispatch thread", e); + } + } private void error(Throwable x) { if (debug) x.printStackTrace(); @@ -76,6 +93,7 @@ class EventQueue implements IEventQueue, Runnable { Runnable r = null; synchronized (this) { while (queue.isEmpty()) { + if (shutdown) return; waiting = true; wait(); } @@ -91,6 +109,7 @@ class EventQueue implements IEventQueue, Runnable { public synchronized void invokeLater(final Runnable r) { assert r != null; + if (shutdown) throw new IllegalStateException("TCF event dispatch is shutdown"); queue.add(r); if (waiting) { waiting = false; @@ -101,7 +120,7 @@ class EventQueue implements IEventQueue, Runnable { public boolean isDispatchThread() { return Thread.currentThread() == thread; } - + public synchronized int getCongestion() { int l0 = job_cnt / 10 - 100; int l1 = queue.size() / 10 - 100; diff --git a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/core/AbstractChannel.java b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/core/AbstractChannel.java index 4eeadb16d..b6c4af42d 100644 --- a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/core/AbstractChannel.java +++ b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/core/AbstractChannel.java @@ -459,7 +459,7 @@ public abstract class AbstractChannel implements IChannel { } else { IChannel.IEventListener[] next = new IChannel.IEventListener[list.length - 1]; - System.arraycopy(list, 0, next, 0, i - 1); + System.arraycopy(list, 0, next, 0, i); System.arraycopy(list, i + 1, next, i, next.length - i); event_listeners.put(service.getName(), next); } diff --git a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/services/IStackTrace.java b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/services/IStackTrace.java index 6c86f9458..cba5b560c 100644 --- a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/services/IStackTrace.java +++ b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/services/IStackTrace.java @@ -30,6 +30,7 @@ public interface IStackTrace extends IService { PROP_NAME = "Name", PROP_FRAME_ADDRESS = "FP", PROP_RETURN_ADDRESS = "RP", + PROP_INSTRUCTION_ADDRESS = "IP", PROP_ARGUMENTS_COUNT = "ArgsCnt", PROP_ARGUMENTS_ADDRESS = "ArgsAddr"; @@ -121,6 +122,14 @@ public interface IStackTrace extends IService { Number getReturnAddress(); /** + * Get address of the next instruction to be executed in this stack frame. + * For top frame it is same as PC register value. + * For other frames it is same as return address of the next frame. + * @return instruction address or null if not a stack frame. + */ + Number getInstructionAddress(); + + /** * Get number of function arguments for this frame. * @return function arguments count. */ |