diff options
author | eutarass | 2008-07-09 16:45:34 +0000 |
---|---|---|
committer | eutarass | 2008-07-09 16:45:34 +0000 |
commit | 67ad4de442d3a8d76c1ecfe777122cb0457017e0 (patch) | |
tree | 87be9df227fe2273ab29351bcbb701b1a10fc97a | |
parent | 7ed998ec71c68d95c0da3e678e403f9e7cf5a298 (diff) | |
download | org.eclipse.tcf-67ad4de442d3a8d76c1ecfe777122cb0457017e0.tar.gz org.eclipse.tcf-67ad4de442d3a8d76c1ecfe777122cb0457017e0.tar.xz org.eclipse.tcf-67ad4de442d3a8d76c1ecfe777122cb0457017e0.zip |
1. Added Protocol.invokeLater() method that dispatches an event with given delay
2. Moved source level stepping and related logic to non-UI plug-in, made it reusable.
3. Added source level stepping support into DSF integration.
27 files changed, 1635 insertions, 913 deletions
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 index 07292e45d..39b19894c 100644 --- 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 @@ -14,9 +14,9 @@ import java.util.HashSet; import java.util.Set; import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.commands.IDebugCommandHandler; 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; @@ -25,7 +25,7 @@ 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 { +abstract class StepCommand implements IDebugCommandHandler { protected final TCFModel model; 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 941100bf1..ef1dd6296 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,226 +10,80 @@ *******************************************************************************/ package org.eclipse.tm.internal.tcf.debug.ui.commands; -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.IStepIntoHandler; +import org.eclipse.tm.internal.tcf.debug.actions.TCFActionStepInto; +import org.eclipse.tm.internal.tcf.debug.model.TCFContextState; +import org.eclipse.tm.internal.tcf.debug.model.TCFSourceRef; import org.eclipse.tm.internal.tcf.debug.ui.Activator; import org.eclipse.tm.internal.tcf.debug.ui.model.TCFModel; 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; +import org.eclipse.tm.tcf.services.IStackTrace.StackTraceContext; +import org.eclipse.tm.tcf.util.TCFDataCache; public class StepIntoCommand extends StepCommand implements IStepIntoHandler { - private static final long TIMEOUT = 10000; - - private static class StepStateMachine extends TCFModel.ContextAction implements IRunControl.RunControlListener { + private static class StepStateMachine extends TCFActionStepInto { 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 final 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()); + IRunControl.RunControlContext ctx, boolean src_step, Runnable done) { + super(model.getLaunch(), ctx, src_step); this.monitor = monitor; - this.ctx = ctx; - this.src_step = src_step; this.done = done; + node = (TCFNodeExecContext)model.getNode(context_id); } - 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; - } - } - 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 { - exit(null); - return; - } - } - } - } - 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); + @Override + protected TCFDataCache<TCFContextState> getContextState() { + if (node == null) return null; + return node.getState(); } - public void contextAdded(RunControlContext[] contexts) { + @Override + protected TCFDataCache<TCFSourceRef> getLineInfo() { + if (frame == null) frame = node.getTopFrame(); + if (frame == null) return null; + return frame.getLineInfo(); } - public void contextChanged(RunControlContext[] contexts) { - for (RunControlContext c : contexts) { - if (c.getID().equals(ctx.getID())) ctx = c; - } + @Override + protected TCFDataCache<StackTraceContext> getStackFrame() { + if (frame == null) frame = node.getTopFrame(); + if (frame == null) return null; + return frame.getStackTraceContext(); } - public void contextException(String context, String msg) { - if (context.equals(ctx.getID())) exit(new Exception(msg)); + @Override + protected int getStackFrameIndex() { + if (frame == null) frame = node.getTopFrame(); + if (frame == null) return 0; + return frame.getFrameNo(); } - public void contextRemoved(String[] context_ids) { - for (String context : context_ids) { - if (context.equals(ctx.getID())) exit(null); - } + @Override + protected TCFDataCache<?> getStackTrace() { + return node.getStackTrace(); } - - 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); + + @Override + protected void exit(Throwable error) { + if (exited) return; + super.exit(error); + if (error != null) { + monitor.setStatus(new Status(IStatus.ERROR, + Activator.PLUGIN_ID, IStatus.OK, "Cannot step", error)); } + done.run(); } } 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 c71a48a73..6a0071dfe 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,292 +10,82 @@ *******************************************************************************/ package org.eclipse.tm.internal.tcf.debug.ui.commands; -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.IStepOverHandler; +import org.eclipse.tm.internal.tcf.debug.actions.TCFActionStepOver; +import org.eclipse.tm.internal.tcf.debug.model.TCFContextState; +import org.eclipse.tm.internal.tcf.debug.model.TCFSourceRef; import org.eclipse.tm.internal.tcf.debug.ui.Activator; import org.eclipse.tm.internal.tcf.debug.ui.model.TCFModel; 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; +import org.eclipse.tm.tcf.services.IStackTrace.StackTraceContext; +import org.eclipse.tm.tcf.util.TCFDataCache; public class StepOverCommand extends StepCommand implements IStepOverHandler { - private static final long TIMEOUT = 10000; - - private static class StepStateMachine extends TCFModel.ContextAction implements IRunControl.RunControlListener { + private static class StepStateMachine extends TCFActionStepOver { 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 final 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()); + super(model.getLaunch(), ctx, src_step); 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; - } - } - 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 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); - } - }); - } - 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 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; - } - } - } - } - 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(); + node = (TCFNodeExecContext)model.getNode(context_id); } - 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; - } + @Override + protected TCFDataCache<TCFContextState> getContextState() { + if (node == null) return null; + return node.getState(); } - public void contextException(String context, String msg) { - if (context.equals(ctx.getID())) exit(new Exception(msg)); + @Override + protected TCFDataCache<TCFSourceRef> getLineInfo() { + if (frame == null) frame = node.getTopFrame(); + if (frame == null) return null; + return frame.getLineInfo(); } - public void contextRemoved(String[] context_ids) { - for (String context : context_ids) { - if (context.equals(ctx.getID())) exit(null); - } + @Override + protected TCFDataCache<StackTraceContext> getStackFrame() { + if (frame == null) frame = node.getTopFrame(); + if (frame == null) return null; + return frame.getStackTraceContext(); } - public void contextResumed(String context) { + @Override + protected int getStackFrameIndex() { + if (frame == null) frame = node.getTopFrame(); + if (frame == null) return 0; + return frame.getFrameNo(); } - 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); - } + @Override + protected TCFDataCache<?> getStackTrace() { + return node.getStackTrace(); } - 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); + @Override + protected void exit(Throwable error) { + if (exited) return; + super.exit(error); + if (error != null) { + monitor.setStatus(new Status(IStatus.ERROR, + Activator.PLUGIN_ID, IStatus.OK, "Cannot step", error)); + } + done.run(); } } 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 45c9aaca6..557114167 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,182 +10,73 @@ *******************************************************************************/ package org.eclipse.tm.internal.tcf.debug.ui.commands; -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.IStepReturnHandler; +import org.eclipse.tm.internal.tcf.debug.actions.TCFActionStepOut; +import org.eclipse.tm.internal.tcf.debug.model.TCFContextState; 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.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; +import org.eclipse.tm.tcf.services.IStackTrace.StackTraceContext; +import org.eclipse.tm.tcf.util.TCFDataCache; public class StepReturnCommand extends StepCommand implements IStepReturnHandler { - private static final long TIMEOUT = 10000; - - private static class StepStateMachine extends TCFModel.ContextAction implements IRunControl.RunControlListener { + private static class StepStateMachine extends TCFActionStepOut { 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 final 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()); + super(model.getLaunch(), ctx); this.monitor = monitor; - this.ctx = ctx; this.done = done; + node = (TCFNodeExecContext)model.getNode(context_id); } - 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")); - } - }); - } - } - 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; - } - } - 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) { - 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) { - } - }); - } - 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(); + @Override + protected TCFDataCache<TCFContextState> getContextState() { + if (node == null) return null; + return node.getState(); } - public void containerResumed(String[] context_ids) { + @Override + protected TCFDataCache<StackTraceContext> getStackFrame() { + if (frame == null) frame = node.getTopFrame(); + if (frame == null) return null; + return frame.getStackTraceContext(); } - 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); + @Override + protected int getStackFrameIndex() { + if (frame == null) frame = node.getTopFrame(); + if (frame == null) return 0; + return frame.getFrameNo(); } - public void contextAdded(RunControlContext[] contexts) { - } - - public void contextChanged(RunControlContext[] contexts) { - for (RunControlContext c : contexts) { - if (c.getID().equals(ctx.getID())) ctx = c; - } + @Override + protected TCFDataCache<?> getStackTrace() { + return node.getStackTrace(); } - - 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); + + @Override + protected void exit(Throwable error) { + if (exited) return; + super.exit(error); + if (error != null) { + monitor.setStatus(new Status(IStatus.ERROR, + Activator.PLUGIN_ID, IStatus.OK, "Cannot step", error)); } - } - - 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); + done.run(); } } 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 d44e61ef6..dc38b51cc 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 @@ -135,6 +135,12 @@ public class TCFAnnotationManager { } updateActiveLaunch(); } + + public void onContextActionsDone(TCFLaunch launch) { + } + + public void onContextActionsStart(TCFLaunch launch) { + } }; private final ISelectionListener selection_listener = new ISelectionListener() { 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 8f69833cc..d3fb608f2 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,9 +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; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.ILaunchConfiguration; @@ -54,6 +52,7 @@ 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; +import org.eclipse.tm.internal.tcf.debug.model.TCFSourceRef; import org.eclipse.tm.internal.tcf.debug.ui.Activator; import org.eclipse.tm.internal.tcf.debug.ui.commands.DisconnectCommand; import org.eclipse.tm.internal.tcf.debug.ui.commands.ResumeCommand; @@ -77,7 +76,11 @@ import org.eclipse.ui.PlatformUI; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.texteditor.ITextEditor; - +/** + * TCFModel represents remote target state as it is known to host. + * The main job of the model is caching remote data, + * keeping the cache in a coherent state, and feeding UI with up-to-date data. + */ public class TCFModel implements IElementContentProvider, IElementLabelProvider, IModelProxyFactory, IColumnPresentationFactory, ISourceDisplay { @@ -88,7 +91,6 @@ 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> future_task_queue = new TreeSet<FutureTask>(); private static final Map<ILaunchConfiguration,IEditorInput> editor_not_found = new HashMap<ILaunchConfiguration,IEditorInput>(); @@ -96,101 +98,8 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, private TCFNodeLaunch launch_node; private boolean disposed; - private int future_task_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; - final long time; - final Runnable run; - - FutureTask(int id, long time, Runnable run) { - this.id = id; - this.time = time; - this.run = run; - } - - public int compareTo(FutureTask x) { - if (x == this) return 0; - if (time < x.time) return -1; - if (time > x.time) return +1; - if (id < x.id) return -1; - if (id > x.id) return +1; - assert false; - return 0; - } - } - - private final Thread future_task_dispatcher = new Thread() { - public void run() { - try { - synchronized (future_task_queue) { - while (!disposed) { - if (future_task_queue.isEmpty()) { - future_task_queue.wait(); - } - else { - long time = System.currentTimeMillis(); - FutureTask t = future_task_queue.first(); - if (t.time > time) { - future_task_queue.wait(t.time - time); - } - else { - future_task_queue.remove(t); - Protocol.invokeLater(t.run); - } - } - } - } - } - catch (Throwable x) { - x.printStackTrace(); - } - } - }; private final IMemory.MemoryListener mem_listener = new IMemory.MemoryListener() { @@ -357,8 +266,6 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, commands.put(IStepIntoHandler.class, new StepIntoCommand(this)); commands.put(IStepOverHandler.class, new StepOverCommand(this)); commands.put(IStepReturnHandler.class, new StepReturnCommand(this)); - future_task_dispatcher.setName("TCF Future Task Dispatcher"); - future_task_dispatcher.start(); } @SuppressWarnings("unchecked") @@ -390,6 +297,13 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, } assert id2node.size() == 0; } + + void onContextActionsStart() { + } + + void onContextActionsDone() { + fireModelChanged(); + } void onProxyInstalled(final TCFModelProxy p) { Protocol.invokeAndWait(new Runnable() { @@ -410,7 +324,7 @@ 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); + launch.removeContextActions(id); TCFNode node = getNode(id); if (node instanceof TCFNodeExecContext) { ((TCFNodeExecContext)node).onContextRemoved(); @@ -451,16 +365,6 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, } void dispose() { - synchronized (future_task_queue) { - disposed = true; - future_task_queue.notify(); - } - try { - future_task_dispatcher.join(); - } - catch (InterruptedException e) { - e.printStackTrace(); - } } void addNode(String id, TCFNode node) { @@ -479,7 +383,7 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, void fireModelChanged() { assert Protocol.isDispatchThread(); - if (context_action_cnt > 0) return; + if (launch.hasPendingContextActions()) return; for (TCFModelProxy p : model_proxies.values()) p.fireModelChanged(); } @@ -502,13 +406,6 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, return id2node.get(id); } - public void invokeLater(long delay, Runnable run) { - 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(); @@ -599,7 +496,7 @@ public class TCFModel implements IElementContentProvider, IElementLabelProvider, final IDebugView view = (IDebugView)window.getActivePage().findView(IDebugUIConstants.ID_DEBUG_VIEW); if (view == null) return; if (!((AbstractDebugView)view).isAvailable()) return; - invokeLater(300, new Runnable() { + Protocol.invokeLater(300, new Runnable() { public void run() { TCFNode node = getNode(node_id); if (node == null) return; 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 2491fa900..cbbbebfc3 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 @@ -38,6 +38,16 @@ public class TCFModelManager { TCFModel model = models.get(launch); if (model != null) model.onDisconnected(); } + + public void onContextActionsDone(TCFLaunch launch) { + TCFModel model = models.get(launch); + if (model != null) model.onContextActionsDone(); + } + + public void onContextActionsStart(TCFLaunch launch) { + TCFModel model = models.get(launch); + if (model != null) model.onContextActionsStart(); + } }; private final ILaunchesListener debug_launch_listener = new ILaunchesListener() { 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 8ec20b19e..503db2851 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 @@ -22,6 +22,8 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.swt.graphics.RGB; +import org.eclipse.tm.internal.tcf.debug.model.TCFContextState; +import org.eclipse.tm.internal.tcf.debug.model.TCFSourceRef; import org.eclipse.tm.internal.tcf.debug.ui.ImageCache; import org.eclipse.tm.tcf.protocol.IChannel; import org.eclipse.tm.tcf.protocol.IToken; @@ -40,17 +42,10 @@ public class TCFNodeExecContext extends TCFNode { private final TCFDataCache<IMemory.MemoryContext> mem_context; private final TCFDataCache<IRunControl.RunControlContext> run_context; - private final TCFDataCache<ContextState> state; + private final TCFDataCache<TCFContextState> state; private final Map<BigInteger,TCFSourceRef> line_info_cache; - private static class ContextState { - boolean suspended; - String suspended_pc; - String suspended_reason; - boolean terminated; - } - private int resumed_cnt; TCFNodeExecContext(TCFNode parent, final String id) { @@ -99,7 +94,7 @@ public class TCFNodeExecContext extends TCFNode { return false; } }; - state = new TCFDataCache<ContextState>(channel) { + state = new TCFDataCache<TCFContextState>(channel) { @Override protected boolean startDataRetrieval() { assert command == null; @@ -114,10 +109,11 @@ public class TCFNodeExecContext extends TCFNode { } command = ctx.getState(new IRunControl.DoneGetState() { public void doneGetState(IToken token, Exception error, boolean suspended, String pc, String reason, Map<String,Object> params) { - ContextState s = new ContextState(); - s.suspended = suspended; - s.suspended_pc = pc; - s.suspended_reason = reason; + TCFContextState s = new TCFContextState(); + s.is_suspended = suspended; + s.suspend_pc = pc; + s.suspend_reason = reason; + s.suspend_params = params; set(token, error, s); } }); @@ -174,8 +170,8 @@ public class TCFNodeExecContext extends TCFNode { IRunControl.RunControlContext ctx = run_context.getData(); if (ctx == null || !ctx.hasState()) return false; if (!state.isValid()) return false; - ContextState s = state.getData(); - return s != null && !s.suspended; + TCFContextState s = state.getData(); + return s != null && !s.is_suspended; } @Override @@ -185,8 +181,8 @@ public class TCFNodeExecContext extends TCFNode { IRunControl.RunControlContext ctx = run_context.getData(); if (ctx == null || !ctx.hasState()) return false; if (!state.isValid()) return false; - ContextState s = state.getData(); - return s != null && s.suspended; + TCFContextState s = state.getData(); + return s != null && s.is_suspended; } @Override @@ -196,10 +192,10 @@ public class TCFNodeExecContext extends TCFNode { IRunControl.RunControlContext ctx = run_context.getData(); if (ctx == null || !ctx.hasState()) return null; if (!state.isValid()) return null; - ContextState s = state.getData(); + TCFContextState s = state.getData(); if (s == null) return null; - if (s.suspended_pc == null) return null; - return new BigInteger(s.suspended_pc); + if (s.suspend_pc == null) return null; + return new BigInteger(s.suspend_pc); } public TCFNodeStackFrame getTopFrame() { @@ -207,6 +203,14 @@ public class TCFNodeExecContext extends TCFNode { if (!children_stack.isValid()) return null; return children_stack.getTopFrame(); } + + public TCFDataCache<TCFContextState> getState() { + return state; + } + + public TCFChildrenStackTrace getStackTrace() { + return children_stack; + } @Override protected void getData(IChildrenCountUpdate result) { @@ -283,7 +287,7 @@ public class TCFNodeExecContext extends TCFNode { label += " (Running)"; } else if (isSuspended()) { - String r = state.getData().suspended_reason; + String r = state.getData().suspend_reason; if (r != null) { label += " (" + r + ")"; } @@ -354,10 +358,11 @@ public class TCFNodeExecContext extends TCFNode { void onContextSuspended(String pc, String reason, Map<String,Object> params) { assert !disposed; - ContextState s = new ContextState(); - s.suspended = true; - s.suspended_pc = pc; - s.suspended_reason = reason; + TCFContextState s = new TCFContextState(); + s.is_suspended = true; + s.suspend_pc = pc; + s.suspend_reason = reason; + s.suspend_params = params; state.reset(s); children_stack.onSuspended(); children_regs.onSuspended(); @@ -367,10 +372,10 @@ public class TCFNodeExecContext extends TCFNode { void onContextResumed() { assert !disposed; - state.reset(new ContextState()); + state.reset(new TCFContextState()); addModelDelta(IModelDelta.STATE); final int cnt = ++resumed_cnt; - model.invokeLater(250, new Runnable() { + Protocol.invokeLater(250, new Runnable() { public void run() { if (cnt != resumed_cnt) return; if (disposed) return; @@ -504,9 +509,9 @@ public class TCFNodeExecContext extends TCFNode { IRunControl.RunControlContext ctx = run_context.getData(); if (ctx != null && ctx.hasState()) { // Thread - ContextState s = state.getData(); - if (s != null && s.terminated) return ImageCache.IMG_THREAD_TERMINATED; - if (s != null && s.suspended) return ImageCache.IMG_THREAD_SUSPENDED; + TCFContextState s = state.getData(); + if (s != null && s.is_terminated) return ImageCache.IMG_THREAD_TERMINATED; + if (s != null && s.is_suspended) return ImageCache.IMG_THREAD_SUSPENDED; return ImageCache.IMG_THREAD_RUNNNIG; } else if (ctx != null) { 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 141b8ad4d..0c1af3b69 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 @@ -21,6 +21,7 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.swt.graphics.RGB; +import org.eclipse.tm.internal.tcf.debug.model.TCFSourceRef; import org.eclipse.tm.internal.tcf.debug.ui.ImageCache; import org.eclipse.tm.tcf.protocol.IChannel; import org.eclipse.tm.tcf.protocol.IToken; @@ -146,6 +147,10 @@ public class TCFNodeStackFrame extends TCFNode { public IMemory.MemoryContext getMemoryContext() { return parent.getMemoryContext(); } + + public TCFDataCache<IStackTrace.StackTraceContext> getStackTraceContext() { + return stack_trace_context; + } @Override public boolean isRunning() { diff --git a/plugins/org.eclipse.tm.tcf.debug/META-INF/MANIFEST.MF b/plugins/org.eclipse.tm.tcf.debug/META-INF/MANIFEST.MF index b1525cfdb..d780b695d 100644 --- a/plugins/org.eclipse.tm.tcf.debug/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.tm.tcf.debug/META-INF/MANIFEST.MF @@ -13,4 +13,5 @@ Bundle-RequiredExecutionEnvironment: J2SE-1.5 Bundle-ActivationPolicy: lazy Eclipse-LazyStart: true Export-Package: org.eclipse.tm.internal.tcf.debug.launch, + org.eclipse.tm.internal.tcf.debug.actions, org.eclipse.tm.internal.tcf.debug.model diff --git a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/actions/TCFAction.java b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/actions/TCFAction.java new file mode 100644 index 000000000..7e298146f --- /dev/null +++ b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/actions/TCFAction.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.internal.tcf.debug.actions; + +import org.eclipse.tm.internal.tcf.debug.model.TCFLaunch; +import org.eclipse.tm.tcf.protocol.Protocol; + +/** + * TCFAction class represents user request to perform some action(s) on + * a remote context, for example, step over line command. + * Such action might require multiple data exchanges with remote target. + * Actions for a particular context should be executed sequentially - + * it does not make sense to execute two step commands concurrently. + * If user requests actions faster then they are executed, + * actions are placed into a FIFO queue. + * + * Clients are expected to implement run() method to perform the action job. + * When the job is done, client code should call done() method. + */ +public abstract class TCFAction implements Runnable { + + protected final TCFLaunch launch; + protected final String context_id; + + public TCFAction(TCFLaunch launch, String context_id) { + assert Protocol.isDispatchThread(); + assert context_id != null; + this.launch = launch; + this.context_id = context_id; + launch.addContextAction(this, context_id); + } + + protected void done() { + assert Protocol.isDispatchThread(); + launch.removeContextAction(this, context_id); + } +} diff --git a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/actions/TCFActionStepInto.java b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/actions/TCFActionStepInto.java new file mode 100644 index 000000000..b2595ae0b --- /dev/null +++ b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/actions/TCFActionStepInto.java @@ -0,0 +1,224 @@ +package org.eclipse.tm.internal.tcf.debug.actions; + +import java.math.BigInteger; +import java.util.Map; + +import org.eclipse.tm.internal.tcf.debug.model.TCFContextState; +import org.eclipse.tm.internal.tcf.debug.model.TCFLaunch; +import org.eclipse.tm.internal.tcf.debug.model.TCFSourceRef; +import org.eclipse.tm.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.IStackTrace; +import org.eclipse.tm.tcf.services.IRunControl.RunControlContext; +import org.eclipse.tm.tcf.util.TCFDataCache; + +public abstract class TCFActionStepInto extends TCFAction implements IRunControl.RunControlListener { + + private static final long TIMEOUT = 10000; + + private final boolean src_step; + private final IRunControl rc = launch.getService(IRunControl.class); + + private IRunControl.RunControlContext ctx; + private TCFDataCache<TCFContextState> state; + private TCFDataCache<TCFSourceRef> line_info; + private TCFSourceRef source_ref; + private BigInteger pc0; + private BigInteger pc1; + private int step_cnt; + + protected boolean exited; + + public TCFActionStepInto(TCFLaunch launch, IRunControl.RunControlContext ctx, boolean src_step) { + super(launch, ctx.getID()); + this.ctx = ctx; + this.src_step = src_step; + } + + protected abstract TCFDataCache<TCFContextState> getContextState(); + protected abstract TCFDataCache<TCFSourceRef> getLineInfo(); + protected abstract TCFDataCache<?> getStackTrace(); + protected abstract TCFDataCache<IStackTrace.StackTraceContext> getStackFrame(); + protected abstract int getStackFrameIndex(); + + public void run() { + if (exited) return; + if (state == null) { + rc.addListener(this); + state = getContextState(); + if (state == null) { + exit(new Exception("Invalid context ID")); + return; + } + if (!ctx.canResume(src_step ? IRunControl.RM_STEP_INTO_LINE : IRunControl.RM_STEP_INTO)) { + Protocol.invokeLater(TIMEOUT, new Runnable() { + public void run() { + exit(new Exception("Time out")); + } + }); + } + } + if (!state.validate()) { + state.wait(this); + return; + } + if (!state.getData().is_suspended) { + 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; + } + TCFDataCache<?> stack_trace = getStackTrace(); + if (!stack_trace.validate()) { + stack_trace.wait(this); + return; + } + if (source_ref == null) { + line_info = getLineInfo(); + if (!line_info.validate()) { + line_info.wait(this); + return; + } + source_ref = line_info.getData(); + if (source_ref == null) { + exit(new Exception("Line info not available")); + return; + } + if (source_ref.error != null) { + exit(source_ref.error); + return; + } + ILineNumbers.CodeArea area = source_ref.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 (getStackFrameIndex() != 0) { + // Stepped out of selected function + exit(null); + return; + } + if (step_cnt > 0) { + TCFDataCache<IStackTrace.StackTraceContext> frame = getStackFrame(); + if (!frame.validate()) { + frame.wait(this); + return; + } + Number addr = frame.getData().getInstructionAddress(); + BigInteger pc = addr instanceof BigInteger ? (BigInteger)addr : new BigInteger(addr.toString()); + if (pc == null || pc0 == null || pc1 == null) { + exit(null); + return; + } + if (pc.compareTo(pc0) < 0 || pc.compareTo(pc1) >= 0) { + if (!line_info.validate()) { + line_info.wait(this); + return; + } + TCFSourceRef ref = line_info.getData(); + if (ref != null && ref.area != null) { + if (isSameLine(source_ref.area, ref.area)) { + source_ref = ref; + ILineNumbers.CodeArea area = source_ref.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; + } + } + } + } + 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; + } + + protected void exit(Throwable error) { + if (exited) return; + rc.removeListener(this); + exited = true; + 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); + } + } +} diff --git a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/actions/TCFActionStepOut.java b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/actions/TCFActionStepOut.java new file mode 100644 index 000000000..b6e64a286 --- /dev/null +++ b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/actions/TCFActionStepOut.java @@ -0,0 +1,170 @@ +package org.eclipse.tm.internal.tcf.debug.actions; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.tm.internal.tcf.debug.model.TCFContextState; +import org.eclipse.tm.internal.tcf.debug.model.TCFLaunch; +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.IRunControl; +import org.eclipse.tm.tcf.services.IStackTrace; +import org.eclipse.tm.tcf.services.IRunControl.RunControlContext; +import org.eclipse.tm.tcf.util.TCFDataCache; + +public abstract class TCFActionStepOut extends TCFAction implements IRunControl.RunControlListener { + + private static final long TIMEOUT = 10000; + + private final IRunControl rc = launch.getService(IRunControl.class); + private final IBreakpoints bps = launch.getService(IBreakpoints.class); + + private IRunControl.RunControlContext ctx; + private TCFDataCache<TCFContextState> state; + private int step_cnt; + private Map<String,Object> bp; + + protected boolean exited; + + public TCFActionStepOut(TCFLaunch launch, IRunControl.RunControlContext ctx) { + super(launch, ctx.getID()); + this.ctx = ctx; + } + + protected abstract TCFDataCache<TCFContextState> getContextState(); + protected abstract TCFDataCache<?> getStackTrace(); + protected abstract TCFDataCache<IStackTrace.StackTraceContext> getStackFrame(); + protected abstract int getStackFrameIndex(); + + public void run() { + if (exited) return; + if (state == null) { + rc.addListener(this); + state = getContextState(); + if (state == null) { + exit(new Exception("Invalid context ID")); + return; + } + if (!ctx.canResume(IRunControl.RM_STEP_OUT)) { + Protocol.invokeLater(TIMEOUT, new Runnable() { + public void run() { + exit(new Exception("Time out")); + } + }); + } + } + if (!state.validate()) { + state.wait(this); + return; + } + if (!state.getData().is_suspended) { + 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; + } + TCFDataCache<?> stack_trace = getStackTrace(); + if (!stack_trace.validate()) { + stack_trace.wait(this); + return; + } + if (getStackFrameIndex() < 0) { + // Stepped out of selected function + exit(null); + } + else if (bps != null && ctx.canResume(IRunControl.RM_RESUME)) { + if (bp == null) { + TCFDataCache<IStackTrace.StackTraceContext> frame = getStackFrame(); + if (!frame.validate()) { + frame.wait(this); + return; + } + Number addr = frame.getData().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) { + 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")); + } + } + + protected void exit(Throwable error) { + 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; + 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); + } +} diff --git a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/actions/TCFActionStepOver.java b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/actions/TCFActionStepOver.java new file mode 100644 index 000000000..f262faa2b --- /dev/null +++ b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/actions/TCFActionStepOver.java @@ -0,0 +1,290 @@ +package org.eclipse.tm.internal.tcf.debug.actions; + +import java.math.BigInteger; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.tm.internal.tcf.debug.model.TCFContextState; +import org.eclipse.tm.internal.tcf.debug.model.TCFLaunch; +import org.eclipse.tm.internal.tcf.debug.model.TCFSourceRef; +import org.eclipse.tm.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.IStackTrace; +import org.eclipse.tm.tcf.services.IRunControl.RunControlContext; +import org.eclipse.tm.tcf.util.TCFDataCache; + +public abstract class TCFActionStepOver extends TCFAction implements IRunControl.RunControlListener { + + private static final long TIMEOUT = 10000; + + private final boolean src_step; + private final IRunControl rc = launch.getService(IRunControl.class); + private final IBreakpoints bps = launch.getService(IBreakpoints.class); + + private IRunControl.RunControlContext ctx; + private TCFDataCache<TCFContextState> state; + private TCFDataCache<TCFSourceRef> line_info; + private TCFSourceRef source_ref; + private BigInteger pc0; + private BigInteger pc1; + private int step_cnt; + private Map<String,Object> bp; + + protected boolean exited; + + public TCFActionStepOver(TCFLaunch launch, IRunControl.RunControlContext ctx, boolean src_step) { + super(launch, ctx.getID()); + this.ctx = ctx; + this.src_step = src_step; + } + + protected abstract TCFDataCache<TCFContextState> getContextState(); + protected abstract TCFDataCache<TCFSourceRef> getLineInfo(); + protected abstract TCFDataCache<?> getStackTrace(); + protected abstract TCFDataCache<IStackTrace.StackTraceContext> getStackFrame(); + protected abstract int getStackFrameIndex(); + + public void run() { + if (exited) return; + if (state == null) { + rc.addListener(this); + state = getContextState(); + if (state == null) { + exit(new Exception("Invalid context ID")); + return; + } + if (!ctx.canResume(src_step ? IRunControl.RM_STEP_OVER_LINE : IRunControl.RM_STEP_OVER)) { + Protocol.invokeLater(TIMEOUT, new Runnable() { + public void run() { + exit(new Exception("Time out")); + } + }); + } + } + if (!state.validate()) { + state.wait(this); + return; + } + if (!state.getData().is_suspended) { + 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; + } + TCFDataCache<?> stack_trace = getStackTrace(); + if (!stack_trace.validate()) { + stack_trace.wait(this); + return; + } + if (src_step && source_ref == null) { + line_info = getLineInfo(); + if (!line_info.validate()) { + line_info.wait(this); + return; + } + source_ref = line_info.getData(); + if (source_ref == null) { + exit(new Exception("Line info not available")); + return; + } + if (source_ref.error != null) { + exit(source_ref.error); + return; + } + ILineNumbers.CodeArea area = source_ref.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 (getStackFrameIndex() != 0) { + if (getStackFrameIndex() < 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 if (bps != null && ctx.canResume(IRunControl.RM_RESUME)) { + if (bp == null) { + TCFDataCache<IStackTrace.StackTraceContext> frame = getStackFrame(); + if (!frame.validate()) { + frame.wait(this); + return; + } + Number addr = frame.getData().getInstructionAddress(); + 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); + } + }); + } + 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 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) { + if (pc0 == null || pc1 == null || state.getData().suspend_pc == null) { + exit(null); + return; + } + assert src_step; + BigInteger pc = new BigInteger(state.getData().suspend_pc); + if (pc.compareTo(pc0) < 0 || pc.compareTo(pc1) >= 0) { + if (!line_info.validate()) { + line_info.wait(this); + return; + } + TCFSourceRef ref = line_info.getData(); + if (ref != null && ref.area != null) { + if (isSameLine(source_ref.area, ref.area)) { + source_ref = ref; + ILineNumbers.CodeArea area = source_ref.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; + } + } + } + } + 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")); + } + } + + protected void exit(Throwable error) { + 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; + 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); + } +} diff --git a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/model/TCFContextState.java b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/model/TCFContextState.java new file mode 100644 index 000000000..3a9dd15a8 --- /dev/null +++ b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/model/TCFContextState.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.internal.tcf.debug.model; + +import java.util.Map; + +public class TCFContextState { + public boolean is_suspended; + public boolean is_terminated; + + public String suspend_pc; + public String suspend_reason; + public Map<String,Object> suspend_params; +} 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 f1c62b3ab..2a355d386 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 @@ -14,6 +14,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -23,6 +24,7 @@ import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.Launch; import org.eclipse.tm.internal.tcf.debug.Activator; +import org.eclipse.tm.internal.tcf.debug.actions.TCFAction; import org.eclipse.tm.internal.tcf.debug.launch.TCFLaunchDelegate; import org.eclipse.tm.tcf.protocol.IChannel; import org.eclipse.tm.tcf.protocol.IPeer; @@ -41,6 +43,9 @@ public class TCFLaunch extends Launch { public void onDisconnected(TCFLaunch launch); + public void onContextActionsStart(TCFLaunch launch); + + public void onContextActionsDone(TCFLaunch launch); } private static final Collection<Listener> listeners = new ArrayList<Listener>(); @@ -55,6 +60,10 @@ public class TCFLaunch extends Launch { private boolean last_context_exited; private ProcessContext process; + private int context_action_cnt; + private final HashMap<String,LinkedList<Runnable>> context_action_queue = + new HashMap<String,LinkedList<Runnable>>(); + public TCFLaunch(ILaunchConfiguration launchConfiguration, String mode) { super(launchConfiguration, mode, null); } @@ -261,13 +270,18 @@ public class TCFLaunch extends Launch { } public void disconnect() throws DebugException { - Protocol.invokeLater(new Runnable() { - public void run() { - if (channel != null && channel.getState() != IChannel.STATE_CLOSED) { - channel.close(); + try { + Protocol.invokeLater(new Runnable() { + public void run() { + if (channel != null && channel.getState() != IChannel.STATE_CLOSED) { + channel.close(); + } } - } - }); + }); + } + catch (IllegalStateException x) { + disconnected = true; + } } public boolean canTerminate() { @@ -307,4 +321,49 @@ public class TCFLaunch extends Launch { }); assert channel.getState() == IChannel.STATE_OPENNING; } + + public void addContextAction(TCFAction action, String context_id) { + assert Protocol.isDispatchThread(); + LinkedList<Runnable> list = context_action_queue.get(context_id); + if (list == null) { + list = new LinkedList<Runnable>(); + context_action_queue.put(context_id, list); + } + list.add(action); + context_action_cnt++; + if (context_action_cnt == 1) { + for (Listener l : listeners) l.onContextActionsStart(this); + } + if (list.getFirst() == action) Protocol.invokeLater(action); + } + + public void removeContextAction(TCFAction action, String context_id) { + assert Protocol.isDispatchThread(); + LinkedList<Runnable> list = context_action_queue.get(context_id); + if (list == null) return; + assert list.getFirst() == action; + list.removeFirst(); + context_action_cnt--; + if (!list.isEmpty()) { + assert context_action_cnt > 0; + Protocol.invokeLater(list.getFirst()); + } + else if (context_action_cnt == 0) { + for (Listener l : listeners) l.onContextActionsDone(this); + } + } + + public void removeContextActions(String context_id) { + assert Protocol.isDispatchThread(); + LinkedList<Runnable> list = context_action_queue.remove(context_id); + if (list == null) return; + context_action_cnt -= list.size(); + if (context_action_cnt == 0) { + for (Listener l : listeners) l.onContextActionsDone(this); + } + } + + public boolean hasPendingContextActions() { + return context_action_cnt > 0; + } } 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/src/org/eclipse/tm/internal/tcf/debug/model/TCFSourceRef.java index 19cbdf74d..00405ed49 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/src/org/eclipse/tm/internal/tcf/debug/model/TCFSourceRef.java @@ -8,7 +8,7 @@ * Contributors: * Wind River Systems - initial API and implementation *******************************************************************************/ -package org.eclipse.tm.internal.tcf.debug.ui.model; +package org.eclipse.tm.internal.tcf.debug.model; import java.math.BigInteger; diff --git a/plugins/org.eclipse.tm.tcf.dsf.ui/src/org/eclipse/tm/internal/tcf/dsf/ui/AdapterFactory.java b/plugins/org.eclipse.tm.tcf.dsf.ui/src/org/eclipse/tm/internal/tcf/dsf/ui/AdapterFactory.java index ff57c02e2..90e55420e 100644 --- a/plugins/org.eclipse.tm.tcf.dsf.ui/src/org/eclipse/tm/internal/tcf/dsf/ui/AdapterFactory.java +++ b/plugins/org.eclipse.tm.tcf.dsf.ui/src/org/eclipse/tm/internal/tcf/dsf/ui/AdapterFactory.java @@ -91,6 +91,7 @@ public class AdapterFactory implements IAdapterFactory, DsfSession.SessionEndedL suspend_command = new DsfSuspendCommand(session); resume_command = new DsfResumeCommand(session); terminate_command = new TcfTerminateCommand(session); + //breakpoint_command = new BreakpointCommand(); //memory_retrieval = new DsfMemoryBlockRetrieval(ITCFConstants.ID_TCF_DEBUG_MODEL, ); session.registerModelAdapter(ISteppingModeTarget.class, steppin_mode_target); diff --git a/plugins/org.eclipse.tm.tcf.dsf.ui/src/org/eclipse/tm/internal/tcf/dsf/ui/viewmodel/ExecutableContextLayoutNode.java b/plugins/org.eclipse.tm.tcf.dsf.ui/src/org/eclipse/tm/internal/tcf/dsf/ui/viewmodel/ExecutableContextLayoutNode.java index acae2eca1..74bac79ba 100644 --- a/plugins/org.eclipse.tm.tcf.dsf.ui/src/org/eclipse/tm/internal/tcf/dsf/ui/viewmodel/ExecutableContextLayoutNode.java +++ b/plugins/org.eclipse.tm.tcf.dsf.ui/src/org/eclipse/tm/internal/tcf/dsf/ui/viewmodel/ExecutableContextLayoutNode.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006 Ericsson and others. + * Copyright (c) 2006, 2008 Ericsson 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 @@ -12,11 +12,17 @@ package org.eclipse.tm.internal.tcf.dsf.ui.viewmodel; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.RejectedExecutionException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; import org.eclipse.dd.dsf.concurrent.DataRequestMonitor; import org.eclipse.dd.dsf.concurrent.DsfRunnable; +import org.eclipse.dd.dsf.concurrent.IDsfStatusConstants; import org.eclipse.dd.dsf.concurrent.RequestMonitor; +import org.eclipse.dd.dsf.datamodel.IDMContext; import org.eclipse.dd.dsf.datamodel.IDMEvent; import org.eclipse.dd.dsf.debug.service.IRunControl; import org.eclipse.dd.dsf.debug.service.IRunControl.IContainerDMContext; @@ -32,12 +38,16 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProv 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.IViewerUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.swt.widgets.Display; +import org.eclipse.tm.internal.tcf.debug.model.TCFContextState; +import org.eclipse.tm.internal.tcf.dsf.ui.Activator; import org.eclipse.tm.internal.tcf.dsf.services.TCFDSFExecutionDMC; import org.eclipse.tm.internal.tcf.dsf.services.TCFDSFRunControl; -import org.eclipse.tm.internal.tcf.dsf.services.TCFDSFRunControlState; +import org.eclipse.tm.internal.tcf.dsf.services.TCFDSFStack; +import org.eclipse.tm.tcf.protocol.Protocol; import org.eclipse.tm.tcf.util.TCFDataCache; @@ -58,12 +68,19 @@ public class ExecutableContextLayoutNode extends AbstractDMVMNode implements IEl @Override protected void updateElementsInSessionThread(final IChildrenUpdate update) { - if (!checkService(IRunControl.class, null, update)) return; + TCFDSFRunControl service = getServicesTracker().getService(TCFDSFRunControl.class); + if (service == null) { + update.setStatus(new Status(IStatus.ERROR, + Activator.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, + "Run Control service not available.", null)); //$NON-NLS-1$ + handleFailedUpdate(update); + return; + } final TCFDSFExecutionDMC dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), TCFDSFExecutionDMC.class); - - getServicesTracker().getService(TCFDSFRunControl.class).getAllContexts(dmc, + + service.getAllContexts(dmc, new DataRequestMonitor<IExecutionDMContext[]>(getSession().getExecutor(), null) { @Override public void handleCompleted() { @@ -89,15 +106,27 @@ public class ExecutableContextLayoutNode extends AbstractDMVMNode implements IEl } catch (RejectedExecutionException e) { for (ILabelUpdate update : updates) { + update.setStatus(new Status(IStatus.ERROR, + Activator.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, + "Cannot execute update request.", e)); //$NON-NLS-1$ handleFailedUpdate(update); } } } private void updateLabelInSessionThread(final ILabelUpdate[] updates) { + TCFDSFRunControl service = getServicesTracker().getService(TCFDSFRunControl.class); + if (service == null) { + for (final ILabelUpdate update : updates) { + update.setStatus(new Status(IStatus.ERROR, + Activator.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, + "Run Control service not available.", null)); //$NON-NLS-1$ + handleFailedUpdate(update); + } + return; + } TCFDataCache<?> pending = null; for (final ILabelUpdate update : updates) { - if (!checkService(TCFDSFRunControl.class, null, update)) continue; TCFDSFExecutionDMC dmc = (TCFDSFExecutionDMC)findDmcInPath(update.getViewerInput(), update.getElementPath(), IContainerDMContext.class); if (!dmc.run_control_context_cache.validate()) pending = dmc.run_control_context_cache; @@ -113,7 +142,6 @@ public class ExecutableContextLayoutNode extends AbstractDMVMNode implements IEl } for (final ILabelUpdate update : updates) { - if (!checkService(TCFDSFRunControl.class, null, update)) continue; TCFDSFExecutionDMC dmc = (TCFDSFExecutionDMC)findDmcInPath(update.getViewerInput(), update.getElementPath(), IContainerDMContext.class); @@ -126,7 +154,7 @@ public class ExecutableContextLayoutNode extends AbstractDMVMNode implements IEl image = IDebugUIConstants.IMG_OBJS_DEBUG_TARGET; } else { - TCFDSFRunControlState state = dmc.run_control_state_cache.getData(); + TCFContextState state = dmc.run_control_state_cache.getData(); if (state != null && state.is_suspended) { image = IDebugUIConstants.IMG_OBJS_THREAD_SUSPENDED; } @@ -150,15 +178,63 @@ public class ExecutableContextLayoutNode extends AbstractDMVMNode implements IEl } return IModelDelta.NO_CHANGE; } + + private List<TCFDSFExecutionDMC> getPath(TCFDSFExecutionDMC dmc) { + List<TCFDSFExecutionDMC> list = new ArrayList<TCFDSFExecutionDMC>(); + while (dmc != null) { + list.add(dmc); + IDMContext[] up = dmc.getParents(); + dmc = null; + for (IDMContext c: up) { + if (c instanceof TCFDSFExecutionDMC) { + dmc = (TCFDSFExecutionDMC)c; + if (dmc.getTcfContextId() == null) dmc = null; + break; + } + } + } + return list; + } - public void buildDelta(Object e, VMDelta parentDelta, int nodeOffset, RequestMonitor requestMonitor) { + public void buildDelta(final Object e, final VMDelta parentDelta, int nodeOffset, final RequestMonitor rm) { if (e instanceof IRunControl.IResumedDMEvent || e instanceof IRunControl.ISuspendedDMEvent) { - parentDelta.addNode(new DMVMContext(((IDMEvent<?>)e).getDMContext()), IModelDelta.STATE); + Protocol.invokeLater(new Runnable() { + public void run() { + TCFDSFStack.TCFFrameDMC frame = null; + TCFDSFExecutionDMC dmc = (TCFDSFExecutionDMC)((IDMEvent<?>)e).getDMContext(); + /* + if (e instanceof IRunControl.ISuspendedDMEvent) { + TCFDSFStack service = getServicesTracker().getService(TCFDSFStack.class); + if (service != null) { + TCFDataCache<?> cache = service.getFramesCache(dmc, null); + if (!cache.validate()) { + cache.wait(this); + return; + } + if (cache.getError() == null) { + frame = service.getTopFrame(dmc); + } + } + } + */ + ModelDelta delta = parentDelta; + List<TCFDSFExecutionDMC> list = getPath(dmc); + for (int i = list.size() - 1; i >= 0; i--) { + delta = delta.addNode(createVMContext(list.get(i)), + i == 0 ? IModelDelta.CONTENT | IModelDelta.STATE : 0); + } + if (frame != null) { + delta = delta.addNode(createVMContext(frame), + IModelDelta.EXPAND | IModelDelta.SELECT); + } + rm.done(); + } + }); + return; } else if (e instanceof IStartedDMEvent || e instanceof IExitedDMEvent) { parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); - //parentDelta.addNode(createVMContext(((IDMEvent<?>)e).getDMContext()), IModelDelta.CONTENT); } - requestMonitor.done(); + rm.done(); } } diff --git a/plugins/org.eclipse.tm.tcf.dsf.ui/src/org/eclipse/tm/internal/tcf/dsf/ui/viewmodel/LaunchVMProvider.java b/plugins/org.eclipse.tm.tcf.dsf.ui/src/org/eclipse/tm/internal/tcf/dsf/ui/viewmodel/LaunchVMProvider.java index 477e3376a..2934513e6 100644 --- a/plugins/org.eclipse.tm.tcf.dsf.ui/src/org/eclipse/tm/internal/tcf/dsf/ui/viewmodel/LaunchVMProvider.java +++ b/plugins/org.eclipse.tm.tcf.dsf.ui/src/org/eclipse/tm/internal/tcf/dsf/ui/viewmodel/LaunchVMProvider.java @@ -46,7 +46,7 @@ implements IDebugEventSetListener, ILaunchesListener2 { addChildNodes(launch_node, new IVMNode[] { threads_node }); IVMNode stack_frames_node = new StackFramesVMNode(this, getSession()); - addChildNodes(threads_node, new IVMNode[] { threads_node, stack_frames_node }); + addChildNodes(threads_node, new IVMNode[] { stack_frames_node, threads_node }); DebugPlugin.getDefault().addDebugEventListener(this); DebugPlugin.getDefault().getLaunchManager().addLaunchListener(this); diff --git a/plugins/org.eclipse.tm.tcf.dsf/src/org/eclipse/tm/internal/tcf/dsf/launch/TCFDSFLaunchSequence.java b/plugins/org.eclipse.tm.tcf.dsf/src/org/eclipse/tm/internal/tcf/dsf/launch/TCFDSFLaunchSequence.java index 6c04def94..21d4ec427 100644 --- a/plugins/org.eclipse.tm.tcf.dsf/src/org/eclipse/tm/internal/tcf/dsf/launch/TCFDSFLaunchSequence.java +++ b/plugins/org.eclipse.tm.tcf.dsf/src/org/eclipse/tm/internal/tcf/dsf/launch/TCFDSFLaunchSequence.java @@ -33,7 +33,7 @@ class TCFDSFLaunchSequence extends Sequence { new Step() { @Override public void execute(RequestMonitor monitor) { - new TCFDSFRunControl(launch.getLaunchConfiguration(), session, channel, monitor); + new TCFDSFRunControl(launch.getLaunchConfiguration(), launch, session, channel, monitor); } }, new Step() { diff --git a/plugins/org.eclipse.tm.tcf.dsf/src/org/eclipse/tm/internal/tcf/dsf/launch/TCFDSFSourceLookupParticipant.java b/plugins/org.eclipse.tm.tcf.dsf/src/org/eclipse/tm/internal/tcf/dsf/launch/TCFDSFSourceLookupParticipant.java index 9cc4290bc..3ac0bd2ff 100644 --- a/plugins/org.eclipse.tm.tcf.dsf/src/org/eclipse/tm/internal/tcf/dsf/launch/TCFDSFSourceLookupParticipant.java +++ b/plugins/org.eclipse.tm.tcf.dsf/src/org/eclipse/tm/internal/tcf/dsf/launch/TCFDSFSourceLookupParticipant.java @@ -12,8 +12,8 @@ package org.eclipse.tm.internal.tcf.dsf.launch; import org.eclipse.core.runtime.CoreException; import org.eclipse.tm.internal.tcf.debug.launch.TCFSourceLookupParticipant; +import org.eclipse.tm.internal.tcf.debug.model.TCFSourceRef; import org.eclipse.tm.internal.tcf.dsf.services.TCFDSFStack.TCFFrameDMC; -import org.eclipse.tm.internal.tcf.dsf.services.TCFDSFStack.TCFFrameData; import org.eclipse.tm.tcf.protocol.Protocol; /** @@ -26,17 +26,31 @@ public class TCFDSFSourceLookupParticipant extends TCFSourceLookupParticipant { public String getSourceName(final Object object) throws CoreException { if (object instanceof TCFFrameDMC) { final Object[] res = new Object[1]; - Protocol.invokeAndWait(new Runnable() { - public void run() { - TCFFrameDMC dmc = (TCFFrameDMC)object; - if (!dmc.frame_data.validate()) { - dmc.frame_data.wait(this); - return; + synchronized (res) { + Protocol.invokeLater(new Runnable() { + public void run() { + TCFFrameDMC dmc = (TCFFrameDMC)object; + if (!dmc.context_cache.validate()) { + dmc.context_cache.wait(this); + return; + } + if (!dmc.source_cache.validate()) { + dmc.source_cache.wait(this); + return; + } + synchronized (res) { + TCFSourceRef ref = dmc.source_cache.getData(); + if (ref != null) res[0] = ref.area; + res.notify(); + } } - TCFFrameData data = dmc.frame_data.getData(); - res[0] = data.code_area; + }); + try { + res.wait(); } - }); + catch (InterruptedException e) { + } + } return super.getSourceName(res[0]); } return super.getSourceName(object); diff --git a/plugins/org.eclipse.tm.tcf.dsf/src/org/eclipse/tm/internal/tcf/dsf/services/TCFDSFExecutionDMC.java b/plugins/org.eclipse.tm.tcf.dsf/src/org/eclipse/tm/internal/tcf/dsf/services/TCFDSFExecutionDMC.java index 067a5253f..3f6d30dc7 100644 --- a/plugins/org.eclipse.tm.tcf.dsf/src/org/eclipse/tm/internal/tcf/dsf/services/TCFDSFExecutionDMC.java +++ b/plugins/org.eclipse.tm.tcf.dsf/src/org/eclipse/tm/internal/tcf/dsf/services/TCFDSFExecutionDMC.java @@ -20,6 +20,7 @@ import org.eclipse.dd.dsf.debug.service.IMemory.IMemoryDMContext; import org.eclipse.dd.dsf.debug.service.IRunControl.IContainerDMContext; import org.eclipse.dd.dsf.debug.service.IRunControl.IExecutionDMContext; import org.eclipse.dd.dsf.service.IDsfService; +import org.eclipse.tm.internal.tcf.debug.model.TCFContextState; import org.eclipse.tm.tcf.protocol.IChannel; import org.eclipse.tm.tcf.protocol.IToken; import org.eclipse.tm.tcf.services.IMemory; @@ -33,7 +34,7 @@ public abstract class TCFDSFExecutionDMC extends AbstractDMContext public final TCFDataCache<IMemory.MemoryContext> memory_context_cache; public final TCFDataCache<RunControlContext> run_control_context_cache; public final TCFDataCache<Map<String,TCFDSFExecutionDMC>> run_control_children_cache; - public final TCFDataCache<TCFDSFRunControlState> run_control_state_cache; + public final TCFDataCache<TCFContextState> run_control_state_cache; TCFDataCache<?> stack_frames_cache; TCFDataCache<?> registers_cache; @@ -104,11 +105,14 @@ public abstract class TCFDSFExecutionDMC extends AbstractDMContext return false; } }; - run_control_state_cache = new TCFDataCache<TCFDSFRunControlState>(channel) { + run_control_state_cache = new TCFDataCache<TCFContextState>(channel) { @Override public boolean startDataRetrieval() { assert command == null; - assert run_control_context_cache.isValid(); + if (!run_control_context_cache.validate()) { + run_control_state_cache.wait(this); + return false; + } RunControlContext c = run_control_context_cache.getData(); if (c == null || !c.hasState()) { reset(null); @@ -117,8 +121,7 @@ public abstract class TCFDSFExecutionDMC extends AbstractDMContext command = c.getState(new IRunControl.DoneGetState() { public void doneGetState(IToken token, Exception err, boolean suspend, String pc, String reason, Map<String,Object> params) { if (command != token) return; - TCFDSFRunControlState data = new TCFDSFRunControlState(); - data.is_running = !suspend; + TCFContextState data = new TCFContextState(); data.is_suspended = suspend; if (suspend) { data.suspend_pc = pc; diff --git a/plugins/org.eclipse.tm.tcf.dsf/src/org/eclipse/tm/internal/tcf/dsf/services/TCFDSFRunControl.java b/plugins/org.eclipse.tm.tcf.dsf/src/org/eclipse/tm/internal/tcf/dsf/services/TCFDSFRunControl.java index b3309a77c..b5ffef8c2 100644 --- a/plugins/org.eclipse.tm.tcf.dsf/src/org/eclipse/tm/internal/tcf/dsf/services/TCFDSFRunControl.java +++ b/plugins/org.eclipse.tm.tcf.dsf/src/org/eclipse/tm/internal/tcf/dsf/services/TCFDSFRunControl.java @@ -31,12 +31,19 @@ import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.model.IMemoryBlockRetrieval; import org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension; +import org.eclipse.tm.internal.tcf.debug.actions.TCFActionStepInto; +import org.eclipse.tm.internal.tcf.debug.actions.TCFActionStepOut; +import org.eclipse.tm.internal.tcf.debug.actions.TCFActionStepOver; import org.eclipse.tm.internal.tcf.debug.model.ITCFConstants; +import org.eclipse.tm.internal.tcf.debug.model.TCFContextState; +import org.eclipse.tm.internal.tcf.debug.model.TCFLaunch; +import org.eclipse.tm.internal.tcf.debug.model.TCFSourceRef; import org.eclipse.tm.internal.tcf.dsf.Activator; import org.eclipse.tm.tcf.protocol.IChannel; import org.eclipse.tm.tcf.protocol.IToken; import org.eclipse.tm.tcf.services.IRunControl; import org.eclipse.tm.tcf.services.IRunControl.RunControlContext; +import org.eclipse.tm.tcf.services.IStackTrace.StackTraceContext; import org.eclipse.tm.tcf.util.TCFDataCache; import org.osgi.framework.BundleContext; @@ -344,7 +351,7 @@ public class TCFDSFRunControl extends AbstractDsfService implements org.eclipse. void onContextSuspended(String pc, String reason, Map<String,Object> params) { assert !disposed; assert !run_control_context_cache.isValid() || run_control_context_cache.getData().hasState(); - TCFDSFRunControlState st = new TCFDSFRunControlState(); + TCFContextState st = new TCFContextState(); st.is_suspended = true; st.suspend_pc = pc; st.suspend_reason = reason; @@ -356,8 +363,7 @@ public class TCFDSFRunControl extends AbstractDsfService implements org.eclipse. void onContextResumed() { assert !disposed; assert !run_control_context_cache.isValid() || run_control_context_cache.getData().hasState(); - TCFDSFRunControlState st = new TCFDSFRunControlState(); - st.is_running = true; + TCFContextState st = new TCFContextState(); run_control_state_cache.reset(st); getSession().dispatchEvent(new ResumedEvent(this), getProperties()); } @@ -400,14 +406,17 @@ public class TCFDSFRunControl extends AbstractDsfService implements org.eclipse. } private final ILaunchConfiguration config; + private final TCFLaunch launch; private final IChannel channel; private final org.eclipse.tm.tcf.services.IRunControl tcf_run_service; private final Map<String,ExecutionDMC> cache = new HashMap<String,ExecutionDMC>(); private final ExecutionDMC root_dmc; - public TCFDSFRunControl(ILaunchConfiguration config, DsfSession session, IChannel channel, final RequestMonitor monitor) { + public TCFDSFRunControl(ILaunchConfiguration config, TCFLaunch launch, + DsfSession session, IChannel channel, final RequestMonitor monitor) { super(session); this.config = config; + this.launch = launch; this.channel = channel; tcf_run_service = channel.getRemoteService(org.eclipse.tm.tcf.services.IRunControl.class); if (tcf_run_service != null) tcf_run_service.addListener(run_listener); @@ -537,7 +546,28 @@ public class TCFDSFRunControl extends AbstractDsfService implements org.eclipse. } else { RunControlContext c = ctx.run_control_context_cache.getData(); - rm.setData(c != null && c.canResume(toTCFStepType(step_type))); + int md = toTCFStepType(step_type); + boolean b = c != null && c.canResume(md); + if (!b && c != null) { + // Check if can emulate desired step type + // TODO: check breakpoints service - it is needed to emulate step commands + switch (md) { + case org.eclipse.tm.tcf.services.IRunControl.RM_STEP_OVER_LINE: + b = c.canResume(org.eclipse.tm.tcf.services.IRunControl.RM_STEP_OVER) || + c.canResume(org.eclipse.tm.tcf.services.IRunControl.RM_STEP_INTO); + break; + case org.eclipse.tm.tcf.services.IRunControl.RM_STEP_OVER: + b = c.canResume(org.eclipse.tm.tcf.services.IRunControl.RM_STEP_INTO); + break; + case org.eclipse.tm.tcf.services.IRunControl.RM_STEP_INTO_LINE: + b = c.canResume(org.eclipse.tm.tcf.services.IRunControl.RM_STEP_INTO); + break; + case org.eclipse.tm.tcf.services.IRunControl.RM_STEP_OUT: + b = c.canResume(org.eclipse.tm.tcf.services.IRunControl.RM_RESUME); + break; + } + } + rm.setData(b); rm.done(); } } @@ -674,35 +704,260 @@ public class TCFDSFRunControl extends AbstractDsfService implements org.eclipse. } return -1; } + + private class StepIntoAction extends TCFActionStepInto { + + private final ExecutionDMC ctx; + private final RequestMonitor monitor; + + private TCFDSFStack.TCFFrameDMC frame; + + StepIntoAction(TCFLaunch launch, ExecutionDMC ctx, RequestMonitor monitor, boolean src_step) { + super(launch, ctx.run_control_context_cache.getData(), src_step); + this.ctx = ctx; + this.monitor = monitor; + ctx.is_stepping++; + } + + @Override + protected TCFDataCache<TCFContextState> getContextState() { + return ctx.run_control_state_cache; + } + + @Override + protected TCFDataCache<TCFSourceRef> getLineInfo() { + if (frame == null) { + TCFDSFStack service = getServicesTracker().getService(TCFDSFStack.class); + if (service == null) return null; + frame = service.getTopFrame(ctx); + if (frame == null) return null; + } + return frame.source_cache; + } - public void step(IExecutionDMContext context, StepType step_type, final RequestMonitor rm) { + @Override + protected TCFDataCache<StackTraceContext> getStackFrame() { + if (frame == null) { + TCFDSFStack service = getServicesTracker().getService(TCFDSFStack.class); + if (service == null) return null; + frame = service.getTopFrame(ctx); + if (frame == null) return null; + } + return frame.context_cache; + } + + @Override + protected int getStackFrameIndex() { + if (frame == null) { + TCFDSFStack service = getServicesTracker().getService(TCFDSFStack.class); + if (service == null) return 0; + frame = service.getTopFrame(ctx); + if (frame == null) return 0; + } + return frame.getLevel(); + } + + @Override + protected TCFDataCache<?> getStackTrace() { + TCFDSFStack service = getServicesTracker().getService(TCFDSFStack.class); + if (service == null) return null; + return service.getFramesCache(ctx, null); + } + + @Override + protected void exit(Throwable error) { + if (exited) return; + super.exit(error); + ctx.is_stepping--; + if (error != null) { + monitor.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + REQUEST_FAILED, "Command error", error)); //$NON-NLS-1$ + } + monitor.done(); + } + } + + private class StepOverAction extends TCFActionStepOver { + + private final ExecutionDMC ctx; + private final RequestMonitor monitor; + + private TCFDSFStack.TCFFrameDMC frame; + + StepOverAction(TCFLaunch launch, ExecutionDMC ctx, RequestMonitor monitor, boolean src_step) { + super(launch, ctx.run_control_context_cache.getData(), src_step); + this.ctx = ctx; + this.monitor = monitor; + ctx.is_stepping++; + } + + @Override + protected TCFDataCache<TCFContextState> getContextState() { + return ctx.run_control_state_cache; + } + + @Override + protected TCFDataCache<TCFSourceRef> getLineInfo() { + if (frame == null) { + TCFDSFStack service = getServicesTracker().getService(TCFDSFStack.class); + if (service == null) return null; + frame = service.getTopFrame(ctx); + if (frame == null) return null; + } + return frame.source_cache; + } + + @Override + protected TCFDataCache<StackTraceContext> getStackFrame() { + if (frame == null) { + TCFDSFStack service = getServicesTracker().getService(TCFDSFStack.class); + if (service == null) return null; + frame = service.getTopFrame(ctx); + if (frame == null) return null; + } + return frame.context_cache; + } + + @Override + protected int getStackFrameIndex() { + if (frame == null) { + TCFDSFStack service = getServicesTracker().getService(TCFDSFStack.class); + if (service == null) return 0; + frame = service.getTopFrame(ctx); + if (frame == null) return 0; + } + return frame.getLevel(); + } + + @Override + protected TCFDataCache<?> getStackTrace() { + TCFDSFStack service = getServicesTracker().getService(TCFDSFStack.class); + if (service == null) return null; + return service.getFramesCache(ctx, null); + } + + @Override + protected void exit(Throwable error) { + if (exited) return; + super.exit(error); + ctx.is_stepping--; + if (error != null) { + monitor.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + REQUEST_FAILED, "Command error", error)); //$NON-NLS-1$ + } + monitor.done(); + } + } + + private class StepOutAction extends TCFActionStepOut { + + private final ExecutionDMC ctx; + private final RequestMonitor monitor; + + private TCFDSFStack.TCFFrameDMC frame; + + StepOutAction(TCFLaunch launch, ExecutionDMC ctx, RequestMonitor monitor) { + super(launch, ctx.run_control_context_cache.getData()); + this.ctx = ctx; + this.monitor = monitor; + ctx.is_stepping++; + } + + @Override + protected TCFDataCache<TCFContextState> getContextState() { + return ctx.run_control_state_cache; + } + + @Override + protected TCFDataCache<StackTraceContext> getStackFrame() { + if (frame == null) { + TCFDSFStack service = getServicesTracker().getService(TCFDSFStack.class); + if (service == null) return null; + frame = service.getTopFrame(ctx); + if (frame == null) return null; + } + return frame.context_cache; + } + + @Override + protected int getStackFrameIndex() { + if (frame == null) { + TCFDSFStack service = getServicesTracker().getService(TCFDSFStack.class); + if (service == null) return 0; + frame = service.getTopFrame(ctx); + if (frame == null) return 0; + } + return frame.getLevel(); + } + + @Override + protected TCFDataCache<?> getStackTrace() { + TCFDSFStack service = getServicesTracker().getService(TCFDSFStack.class); + if (service == null) return null; + return service.getFramesCache(ctx, null); + } + + @Override + protected void exit(Throwable error) { + if (exited) return; + super.exit(error); + ctx.is_stepping--; + if (error != null) { + monitor.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + REQUEST_FAILED, "Command error", error)); //$NON-NLS-1$ + } + monitor.done(); + } + } + + public void step(final IExecutionDMContext context, final StepType step_type, final RequestMonitor rm) { if (context instanceof ExecutionDMC) { final ExecutionDMC ctx = (ExecutionDMC)context; - if (ctx.run_control_context_cache.isValid()) { - RunControlContext c = ctx.run_control_context_cache.getData(); - if (c != null) { - int md = toTCFStepType(step_type); - if (md < 0) { - rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, - NOT_SUPPORTED, "Invalid step type", null)); //$NON-NLS-1$ - rm.done(); + if (!ctx.run_control_context_cache.validate()) { + ctx.run_control_context_cache.wait(new Runnable() { + public void run() { + step(context, step_type, rm); } - else { - c.resume(md, 1, new org.eclipse.tm.tcf.services.IRunControl.DoneCommand() { - public void doneCommand(IToken token, Exception error) { - if (rm.isCanceled()) return; - if (error != null) { - rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, - REQUEST_FAILED, "Command error", error)); //$NON-NLS-1$ - } - ctx.is_stepping--; - rm.done(); - } - }); - ctx.is_stepping++; + }); + return; + } + if (ctx.run_control_context_cache.getError() != null) { + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + REQUEST_FAILED, "Data error", ctx.run_control_context_cache.getError())); //$NON-NLS-1$ + rm.done(); + return; + } + RunControlContext c = ctx.run_control_context_cache.getData(); + if (c != null) { + int md = toTCFStepType(step_type); + if (md < 0) { + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + NOT_SUPPORTED, "Invalid step type", null)); //$NON-NLS-1$ + rm.done(); + } + else { + switch (md) { + case org.eclipse.tm.tcf.services.IRunControl.RM_STEP_INTO: + new StepIntoAction(launch, ctx, rm, false); + return; + case org.eclipse.tm.tcf.services.IRunControl.RM_STEP_INTO_LINE: + new StepIntoAction(launch, ctx, rm, true); + return; + case org.eclipse.tm.tcf.services.IRunControl.RM_STEP_OVER: + new StepOverAction(launch, ctx, rm, false); + return; + case org.eclipse.tm.tcf.services.IRunControl.RM_STEP_OVER_LINE: + new StepOverAction(launch, ctx, rm, true); + return; + case org.eclipse.tm.tcf.services.IRunControl.RM_STEP_OUT: + new StepOutAction(launch, ctx, rm); + return; } - return; + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + NOT_SUPPORTED, "Invalid step type", null)); //$NON-NLS-1$ + rm.done(); } + return; } rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$ @@ -806,7 +1061,7 @@ public class TCFDSFRunControl extends AbstractDsfService implements org.eclipse. RunControlContext c = ctx.run_control_context_cache.getData(); if (c != null && c.hasState()) { if (ctx.is_resuming == 0 && ctx.is_stepping == 0 && ctx.run_control_state_cache.isValid()) { - TCFDSFRunControlState st = ctx.run_control_state_cache.getData(); + TCFContextState st = ctx.run_control_state_cache.getData(); if (st != null) r = st.is_suspended; } } @@ -820,7 +1075,7 @@ public class TCFDSFRunControl extends AbstractDsfService implements org.eclipse. ExecutionDMC ctx = (ExecutionDMC)dmc; StateChangeReason r = StateChangeReason.UNKNOWN; if (ctx.run_control_state_cache.isValid()) { - TCFDSFRunControlState st = ctx.run_control_state_cache.getData(); + TCFContextState st = ctx.run_control_state_cache.getData(); if (st != null && st.suspend_reason != null) { r = toStateChangeReason(st.suspend_reason); } diff --git a/plugins/org.eclipse.tm.tcf.dsf/src/org/eclipse/tm/internal/tcf/dsf/services/TCFDSFRunControlState.java b/plugins/org.eclipse.tm.tcf.dsf/src/org/eclipse/tm/internal/tcf/dsf/services/TCFDSFRunControlState.java deleted file mode 100644 index 56ce4fd6e..000000000 --- a/plugins/org.eclipse.tm.tcf.dsf/src/org/eclipse/tm/internal/tcf/dsf/services/TCFDSFRunControlState.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.eclipse.tm.internal.tcf.dsf.services; - -import java.math.BigInteger; -import java.util.Map; - -public class TCFDSFRunControlState { - - public boolean is_suspended; - public boolean is_running; - public String suspend_pc; - public String suspend_reason; - public Map<String,Object> suspend_params; - - public TCFAddress getPC() { - if (suspend_pc == null) return null; - return new TCFAddress(new BigInteger(suspend_pc)); - } -} - diff --git a/plugins/org.eclipse.tm.tcf.dsf/src/org/eclipse/tm/internal/tcf/dsf/services/TCFDSFStack.java b/plugins/org.eclipse.tm.tcf.dsf/src/org/eclipse/tm/internal/tcf/dsf/services/TCFDSFStack.java index 841ebe8ab..d3302f0c0 100644 --- a/plugins/org.eclipse.tm.tcf.dsf/src/org/eclipse/tm/internal/tcf/dsf/services/TCFDSFStack.java +++ b/plugins/org.eclipse.tm.tcf.dsf/src/org/eclipse/tm/internal/tcf/dsf/services/TCFDSFStack.java @@ -29,6 +29,7 @@ import org.eclipse.dd.dsf.debug.service.IRunControl.StateChangeReason; import org.eclipse.dd.dsf.service.AbstractDsfService; import org.eclipse.dd.dsf.service.DsfServiceEventHandler; import org.eclipse.dd.dsf.service.DsfSession; +import org.eclipse.tm.internal.tcf.debug.model.TCFSourceRef; import org.eclipse.tm.internal.tcf.dsf.Activator; import org.eclipse.tm.tcf.protocol.IChannel; import org.eclipse.tm.tcf.protocol.IToken; @@ -45,16 +46,17 @@ public class TCFDSFStack extends AbstractDsfService implements IStack { public final String id; public final TCFDSFExecutionDMC exe_dmc; - public final TCFDataCache<TCFFrameData> frame_data; + public final TCFDataCache<IStackTrace.StackTraceContext> context_cache; + public final TCFDataCache<TCFSourceRef> source_cache; int level; - TCFFrameData prev_data; + TCFSourceRef prev_data; public TCFFrameDMC(final TCFDSFExecutionDMC exe_dmc, final String id) { super(TCFDSFStack.this.getSession().getId(), new IDMContext[] { exe_dmc }); this.id = id; this.exe_dmc = exe_dmc; - frame_data = new TCFDataCache<TCFFrameData>(channel) { + context_cache = new TCFDataCache<IStackTrace.StackTraceContext>(channel) { @Override public boolean startDataRetrieval() { @@ -66,41 +68,53 @@ public class TCFDSFStack extends AbstractDsfService implements IStack { command = tcf_stk_service.getContext(new String[]{ id }, new IStackTrace.DoneGetContext() { public void doneGetContext(IToken token, Exception err, IStackTrace.StackTraceContext[] context) { if (command != token) return; - TCFFrameData data = null; - TCFAddress a = null; - Number n = context[0].getInstructionAddress(); - if (n != null) a = new TCFAddress(n); - // Optimization: skip source position lookup if same address - if (prev_data != null && prev_data.address != null && prev_data.address.equals(a)) { - data = prev_data; - data.context = context[0]; - data.level = level; - } - else { - data = new TCFFrameData(); - data.context = context[0]; - data.address = a; - data.level = level; - if (!getSourcePos(data)) return; - } - set(token, err, prev_data = data); + IStackTrace.StackTraceContext ctx = null; + if (context != null && context.length > 0) ctx = context[0]; + set(token, err, ctx); } }); return false; } + }; + + source_cache = new TCFDataCache<TCFSourceRef>(channel) { + + @Override + protected boolean startDataRetrieval() { + if (!context_cache.validate()) { + context_cache.wait(this); + return false; + } + IStackTrace.StackTraceContext ctx = context_cache.getData(); + Number n = ctx.getInstructionAddress(); + BigInteger a = null; + if (n != null) a = new BigInteger(n.toString()); + // Optimization: skip source position lookup if same address + TCFSourceRef data = null; + if (prev_data != null && prev_data.address != null && prev_data.address.equals(a)) { + data = prev_data; + } + else { + data = new TCFSourceRef(); + data.address = a; + if (!getSourcePos(data)) return false; + } + set(null, null, prev_data = data); + return true; + } - private boolean getSourcePos(final TCFFrameData data) { + private boolean getSourcePos(final TCFSourceRef data) { if (tcf_lns_service == null) return true; if (data.address == null) return true; - BigInteger a1 = data.address.getValue(); - BigInteger a2 = data.address.add(1).getValue(); + BigInteger a1 = data.address; + BigInteger a2 = data.address.add(BigInteger.valueOf(1)); command = tcf_lns_service.mapToSource(exe_dmc.getTcfContextId(), a1, a2, new ILineNumbers.DoneMapToSource() { public void doneMapToSource(IToken token, Exception err, CodeArea[] areas) { if (command != token) return; if (areas != null && areas.length > 0) { for (ILineNumbers.CodeArea area : areas) { - if (data.code_area == null || area.start_line < data.code_area.start_line) { - data.code_area = area; + if (data.area == null || area.start_line < data.area.start_line) { + data.area = area; } } } @@ -140,12 +154,20 @@ public class TCFDSFStack extends AbstractDsfService implements IStack { public static class TCFFrameData implements IFrameDMData { - public IStackTrace.StackTraceContext context; - public IAddress address; - public int level; - public String function; - public Throwable src_pos_error; - public ILineNumbers.CodeArea code_area; + public final IStackTrace.StackTraceContext context; + public final IAddress address; + public final int level; + public final String function; + public final ILineNumbers.CodeArea code_area; + + TCFFrameData(TCFFrameDMC dmc) { + context = dmc.context_cache.getData(); + TCFSourceRef ref = dmc.source_cache.getData(); + address = new TCFAddress(ref.address); + level = dmc.getLevel(); + function = null; + code_area = ref.area; + } public IAddress getAddress() { return address; @@ -191,6 +213,19 @@ public class TCFDSFStack extends AbstractDsfService implements IStack { @Override public boolean startDataRetrieval() { assert command == null; + if (!dmc.run_control_context_cache.validate()) { + dmc.run_control_context_cache.wait(this); + return false; + } + if (dmc.run_control_context_cache.getError() != null) { + set(null, dmc.run_control_context_cache.getError(), null); + return true; + } + org.eclipse.tm.tcf.services.IRunControl.RunControlContext ctx = dmc.run_control_context_cache.getData(); + if (ctx == null || !ctx.hasState()) { + set(null, new Exception("DMC does not have a stack"), null); //$NON-NLS-1$ + return true; + } if (tcf_stk_service == null) { HashMap<String,TCFFrameDMC> data = new HashMap<String,TCFFrameDMC>(); top_frame_id = "TopFrame:" + dmc.getTcfContextId(); @@ -225,7 +260,10 @@ public class TCFDSFStack extends AbstractDsfService implements IStack { void invalidateFrames() { reset(); - for (TCFFrameDMC dmc : frame_pool.values()) dmc.frame_data.reset(); + for (TCFFrameDMC dmc : frame_pool.values()) { + dmc.context_cache.reset(); + dmc.source_cache.reset(); + } } void dispose() { @@ -283,22 +321,35 @@ public class TCFDSFStack extends AbstractDsfService implements IStack { public void getFrameData(final IFrameDMContext dmc, final DataRequestMonitor<IFrameDMData> rm) { if (dmc instanceof TCFFrameDMC) { final TCFFrameDMC frame_dmc = (TCFFrameDMC)dmc; - TCFDataCache<TCFFrameData> cache = frame_dmc.frame_data; - if (!cache.validate()) { - cache.wait(new Runnable() { + if (!frame_dmc.context_cache.validate()) { + frame_dmc.context_cache.wait(new Runnable() { public void run() { getFrameData(dmc, rm); } }); return; } - if (cache.getError() != null) { + if (frame_dmc.context_cache.getError() != null) { rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, - REQUEST_FAILED, "Data error", cache.getError())); //$NON-NLS-1$ + REQUEST_FAILED, "Data error", frame_dmc.context_cache.getError())); //$NON-NLS-1$ + rm.done(); + return; + } + if (!frame_dmc.source_cache.validate()) { + frame_dmc.source_cache.wait(new Runnable() { + public void run() { + getFrameData(dmc, rm); + } + }); + return; + } + if (frame_dmc.source_cache.getError() != null) { + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + REQUEST_FAILED, "Data error", frame_dmc.source_cache.getError())); //$NON-NLS-1$ rm.done(); return; } - rm.setData(cache.getData()); + rm.setData(new TCFFrameData(frame_dmc)); rm.done(); } else { @@ -308,54 +359,32 @@ public class TCFDSFStack extends AbstractDsfService implements IStack { } } - private TCFDataCache<?> createFramesCache(TCFDSFExecutionDMC exe, DataRequestMonitor<?> rm) { + public TCFDataCache<?> getFramesCache(TCFDSFExecutionDMC exe, DataRequestMonitor<?> rm) { if (tcf_stk_service == null) { - rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, - INVALID_HANDLE, "Stack trace service is not available", null)); //$NON-NLS-1$ - rm.done(); + if (rm != null) { + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + INVALID_HANDLE, "Stack trace service is not available", null)); //$NON-NLS-1$ + rm.done(); + } return null; } if (exe.isDisposed()) { - rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, - INVALID_HANDLE, "Disposed DMC", null)); //$NON-NLS-1$ - rm.done(); - return null; - } - if (!exe.run_control_context_cache.validate()) { - return exe.run_control_context_cache; - } - if (exe.run_control_context_cache.getError() != null) { - rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, - REQUEST_FAILED, "Data error", exe.run_control_context_cache.getError())); //$NON-NLS-1$ - rm.done(); - return null; - } - org.eclipse.tm.tcf.services.IRunControl.RunControlContext ctx = exe.run_control_context_cache.getData(); - if (ctx == null || !ctx.hasState()) { - rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, - INVALID_HANDLE, "DMC does not have a stack", null)); //$NON-NLS-1$ - rm.done(); + if (rm != null) { + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + INVALID_HANDLE, "Disposed DMC", null)); //$NON-NLS-1$ + rm.done(); + } return null; } if (exe.stack_frames_cache == null) exe.stack_frames_cache = new FramesCache(channel, exe); - return exe.stack_frames_cache; - + return (FramesCache)exe.stack_frames_cache; } public void getFrames(final IDMContext dmc, final DataRequestMonitor<IFrameDMContext[]> rm) { if (dmc instanceof TCFDSFExecutionDMC) { TCFDSFExecutionDMC exe = (TCFDSFExecutionDMC)dmc; - TCFDataCache<?> cache0 = createFramesCache(exe, rm); - if (cache0 == null) return; - if (cache0 != exe.stack_frames_cache) { - cache0.wait(new Runnable() { - public void run() { - getFrames(dmc, rm); - } - }); - return; - } - FramesCache cache = (FramesCache)cache0; + FramesCache cache = (FramesCache)getFramesCache(exe, rm); + if (cache == null) return; if (!cache.validate()) { cache.wait(new Runnable() { public void run() { @@ -399,17 +428,8 @@ public class TCFDSFStack extends AbstractDsfService implements IStack { public void getStackDepth(final IDMContext dmc, final int maxDepth, final DataRequestMonitor<Integer> rm) { if (dmc instanceof TCFDSFExecutionDMC) { TCFDSFExecutionDMC exe = (TCFDSFExecutionDMC)dmc; - TCFDataCache<?> cache0 = createFramesCache(exe, rm); - if (cache0 == null) return; - if (cache0 != exe.stack_frames_cache) { - cache0.wait(new Runnable() { - public void run() { - getStackDepth(dmc, maxDepth, rm); - } - }); - return; - } - FramesCache cache = (FramesCache)cache0; + FramesCache cache = (FramesCache)getFramesCache(exe, rm); + if (cache == null) return; if (!cache.validate()) { cache.wait(new Runnable() { public void run() { @@ -437,17 +457,22 @@ public class TCFDSFStack extends AbstractDsfService implements IStack { public void getTopFrame(final IDMContext dmc, final DataRequestMonitor<IFrameDMContext> rm) { if (dmc instanceof TCFDSFExecutionDMC) { TCFDSFExecutionDMC exe = (TCFDSFExecutionDMC)dmc; - TCFDataCache<?> cache0 = createFramesCache(exe, rm); - if (cache0 == null) return; - if (cache0 != exe.stack_frames_cache) { - cache0.wait(new Runnable() { + FramesCache cache = (FramesCache)getFramesCache(exe, rm); + if (cache == null) return; + if (!cache.validate()) { + cache.wait(new Runnable() { public void run() { getTopFrame(dmc, rm); } }); return; } - FramesCache cache = (FramesCache)cache0; + if (cache.getError() != null) { + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + REQUEST_FAILED, "Data error", cache.getError())); //$NON-NLS-1$ + rm.done(); + return; + } rm.setData(cache.createFrameDMC(cache.top_frame_id, 0)); rm.done(); } @@ -457,6 +482,14 @@ public class TCFDSFStack extends AbstractDsfService implements IStack { rm.done(); } } + + public TCFFrameDMC getTopFrame(TCFDSFExecutionDMC exe) { + FramesCache cache = (FramesCache)getFramesCache(exe, null); + assert cache != null; + assert cache.isValid(); + assert cache.getError() == null; + return cache.createFrameDMC(cache.top_frame_id, 0); + } public void getVariableData(IVariableDMContext variableDmc, DataRequestMonitor<IVariableDMData> rm) { // TODO model data for local variables diff --git a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/protocol/Protocol.java b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/protocol/Protocol.java index df0ee8144..8d77dea6c 100644 --- a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/protocol/Protocol.java +++ b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/protocol/Protocol.java @@ -11,6 +11,7 @@ package org.eclipse.tm.tcf.protocol; import java.util.ArrayList; +import java.util.TreeSet; import org.eclipse.tm.internal.tcf.core.LocalPeer; import org.eclipse.tm.internal.tcf.core.Transport; @@ -24,21 +25,82 @@ import org.eclipse.tm.tcf.services.ILocator; * 2. local instance of Locator service, which maintains a list of available targets; * 3. list of open communication channels. * + * It also provides utility methods for posting asynchronous events, + * including delayed events (timers). + * + * Before TCF can be used, it should be given an object implementing IEventQueue interface: + * @see #setEventQueue + * * @noextend This class is not intended to be subclassed by clients. * @noinstantiate This class is not intended to be instantiated by clients. */ public final class Protocol { private static IEventQueue event_queue; + private static final TreeSet<Timer> timer_queue = new TreeSet<Timer>(); + private static int timer_cnt; + private static class Timer implements Comparable<Timer>{ + final int id; + final long time; + final Runnable run; + + Timer(long time, Runnable run) { + this.id = timer_cnt++; + this.time = time; + this.run = run; + } + + public int compareTo(Timer x) { + if (x == this) return 0; + if (time < x.time) return -1; + if (time > x.time) return +1; + if (id < x.id) return -1; + if (id > x.id) return +1; + assert false; + return 0; + } + } + + private static final Thread timer_dispatcher = new Thread() { + public void run() { + try { + synchronized (timer_queue) { + while (true) { + if (timer_queue.isEmpty()) { + timer_queue.wait(); + } + else { + long time = System.currentTimeMillis(); + Timer t = timer_queue.first(); + if (t.time > time) { + timer_queue.wait(t.time - time); + } + else { + timer_queue.remove(t); + invokeLater(t.run); + } + } + } + } + } + catch (IllegalStateException x) { + // Dispatch is shut down, exit this thread + } + catch (Throwable x) { + x.printStackTrace(); + } + } + }; + private static final ArrayList<CongestionMonitor> congestion_monitors = new ArrayList<CongestionMonitor>(); /** - * Before TCF can be used it should be given an object implementing IEventQueue interface. + * Before TCF can be used, it should be given an object implementing IEventQueue interface. * The implementation maintains a queue of objects implementing Runnable interface and * executes <code>run</code> methods of that objects in a sequence by a single thread. * The thread in referred as TCF event dispatch thread. Objects in the queue are called TCF events. - * Executing <code>run</code> method of an event is also called dispatching of event. + * Executing <code>run</code> method of an event is also called dispatching of the event. * * Only few methods in TCF APIs are thread safe - can be invoked from any thread. * If a method description does not say "can be invoked from any thread" explicitly - @@ -57,6 +119,9 @@ public final class Protocol { new LocalPeer(); } }); + timer_dispatcher.setName("TCF Timer Dispatcher"); + timer_dispatcher.setDaemon(true); + timer_dispatcher.start(); } /** @@ -78,7 +143,7 @@ public final class Protocol { } /** - * Causes <code>runnable</code> to have its <code>run</code> + * Causes <code>runnable</code> event to have its <code>run</code> * method called in the dispatch thread of the framework. * Events are dispatched in same order as queued. * If invokeLater is called from the dispatching thread @@ -95,6 +160,31 @@ public final class Protocol { } /** + * Causes <code>runnable</code> event to have its <code>run</code> + * method called in the dispatch thread of the framework. + * The event is dispatched after given delay. + * + * This method can be invoked from any thread. + * + * @param delay milliseconds to delay event dispatch. + * If delay <= 0 the event is posted into the + * "ready" queue without delay. + * @param runnable the <code>Runnable</code> whose <code>run</code> + * method should be executed asynchronously. + */ + public static void invokeLater(long delay, Runnable runnable) { + if (delay <= 0) { + event_queue.invokeLater(runnable); + } + else { + synchronized (timer_queue) { + timer_queue.add(new Timer(System.currentTimeMillis() + delay, runnable)); + timer_queue.notify(); + } + } + } + + /** * Causes <code>runnable</code> to have its <code>run</code> * method called in the dispatch thread of the framework. * Calling thread is suspended until the method is executed. @@ -186,14 +276,14 @@ public final class Protocol { } /** - * Call back after TCF messages sent by this host up to this moment are delivered + * Call back after all TCF messages sent by this host up to this moment are delivered * to their intended target. This method is intended for synchronization of messages * across multiple channels. * * Note: Cross channel synchronization can reduce performance and throughput. * Most clients don't need cross channel synchronization and should not call this method. * - * @param done will be executed by dispatch thread after communication + * @param done will be executed by dispatch thread after pending communication * messages are delivered to corresponding targets. */ public static void sync(Runnable done) { |