Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/org.eclipse.tcf.debug/src/org')
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/Activator.java88
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/actions/TCFAction.java62
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/actions/TCFActionStepInto.java268
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/actions/TCFActionStepOut.java216
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/actions/TCFActionStepOver.java368
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFLaunchDelegate.java350
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFLocalAgent.java223
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFSourceLookupDirector.java46
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFSourceLookupParticipant.java153
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFSourcePathComputerDelegate.java61
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFUserDefPeer.java98
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/ITCFBreakpointListener.java19
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/ITCFConstants.java20
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFBreakpoint.java135
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFBreakpointsModel.java676
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFBreakpointsStatus.java151
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFContextState.java21
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFError.java70
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFFunctionRef.java40
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFLaunch.java1318
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFMemoryRegion.java69
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFSourceRef.java42
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/ITCFTest.java29
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/Main.java164
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/RunControl.java280
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TCFTestSuite.java312
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestAttachTerminate.java152
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestEcho.java94
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestEchoERR.java117
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestEchoFP.java81
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestExpressions.java650
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestFileSystem.java482
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestPathMap.java182
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestRCBP1.java1729
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestStreams.java305
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestSysMonitor.java119
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestTerminals.java507
37 files changed, 9697 insertions, 0 deletions
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/Activator.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/Activator.java
new file mode 100644
index 000000000..7aa6d5f0c
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/Activator.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 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;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Plugin;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.tm.internal.tcf.debug.launch.TCFLocalAgent;
+import org.eclipse.tm.internal.tcf.debug.launch.TCFUserDefPeer;
+import org.eclipse.tm.internal.tcf.debug.model.TCFBreakpointsModel;
+import org.eclipse.tm.tcf.protocol.Protocol;
+import org.osgi.framework.BundleContext;
+
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends Plugin {
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "org.eclipse.tm.tcf.debug";
+
+ // The shared instance
+ private static Activator plugin;
+ private static TCFBreakpointsModel bp_model;
+
+ public Activator() {
+ plugin = this;
+ }
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ bp_model = new TCFBreakpointsModel();
+ Protocol.invokeLater(new Runnable() {
+
+ public void run() {
+ TCFUserDefPeer.loadPeers();
+ }
+ });
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ bp_model.dispose();
+ bp_model = null;
+ TCFLocalAgent.destroy();
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static Activator getDefault() {
+ return plugin;
+ }
+
+ public static TCFBreakpointsModel getBreakpointsModel() {
+ return bp_model;
+ }
+
+ /**
+ * Send error message into Eclipse log.
+ * @param msg - error message test
+ * @param err - exception
+ */
+ public static void log(String msg, Throwable err) {
+ if (plugin == null || plugin.getLog() == null) {
+ System.err.println(msg);
+ if (err != null) err.printStackTrace();
+ }
+ else {
+ plugin.getLog().log(new Status(IStatus.ERROR,
+ plugin.getBundle().getSymbolicName(), IStatus.OK, msg, err));
+ }
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/actions/TCFAction.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/actions/TCFAction.java
new file mode 100644
index 000000000..cba7c80c6
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/actions/TCFAction.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 ctx_id;
+
+ protected boolean aborted;
+
+ public TCFAction(TCFLaunch launch, String ctx_id) {
+ assert Protocol.isDispatchThread();
+ this.launch = launch;
+ this.ctx_id = ctx_id;
+ launch.addContextAction(this);
+ }
+
+ public void abort() {
+ aborted = true;
+ }
+
+ public String getContextID() {
+ return ctx_id;
+ }
+
+ public int getPriority() {
+ return 0;
+ }
+
+ public void setActionResult(String id, String result) {
+ launch.setContextActionResult(id, result);
+ }
+
+ public void done() {
+ assert Protocol.isDispatchThread();
+ launch.removeContextAction(this);
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/actions/TCFActionStepInto.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/actions/TCFActionStepInto.java
new file mode 100644
index 000000000..0966ce985
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/actions/TCFActionStepInto.java
@@ -0,0 +1,268 @@
+/*******************************************************************************
+ * Copyright (c) 2010 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 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.JSON;
+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.util.TCFDataCache;
+
+public abstract class TCFActionStepInto extends TCFAction implements IRunControl.RunControlListener {
+
+ private boolean step_line;
+ private boolean step_back;
+ private final IRunControl rc = launch.getService(IRunControl.class);
+
+ private IRunControl.RunControlContext ctx;
+ private TCFDataCache<TCFContextState> state;
+ private TCFSourceRef source_ref;
+ private BigInteger pc0;
+ private BigInteger pc1;
+ private int step_cnt;
+ private boolean second_step_back;
+ private boolean final_step;
+
+ protected boolean exited;
+
+ public TCFActionStepInto(TCFLaunch launch, IRunControl.RunControlContext ctx, boolean step_line, boolean back_step) {
+ super(launch, ctx.getID());
+ this.ctx = ctx;
+ this.step_line = step_line;
+ this.step_back = back_step;
+ }
+
+ protected abstract TCFDataCache<TCFContextState> getContextState();
+ protected abstract TCFDataCache<TCFSourceRef> getLineInfo();
+ protected abstract TCFDataCache<?> getStackTrace();
+
+ public void run() {
+ if (exited) return;
+ try {
+ runAction();
+ }
+ catch (Throwable x) {
+ exit(x);
+ }
+ }
+
+ private void setSourceRef(TCFSourceRef ref) {
+ ILineNumbers.CodeArea area = ref.area;
+ if (area != null) {
+ pc0 = JSON.toBigInteger(area.start_address);
+ pc1 = JSON.toBigInteger(area.end_address);
+ }
+ else {
+ pc0 = null;
+ pc1 = null;
+ }
+ source_ref = ref;
+ }
+
+ private void runAction() {
+ if (aborted) {
+ exit(null);
+ return;
+ }
+ if (state == null) {
+ rc.addListener(this);
+ state = getContextState();
+ if (state == null) {
+ exit(new Exception("Invalid context ID"));
+ return;
+ }
+ }
+ if (!state.validate(this)) return;
+ if (state.getData() == null || !state.getData().is_suspended) {
+ Throwable error = state.getError();
+ if (error == null) error = new Exception("Context is not suspended");
+ exit(error);
+ return;
+ }
+ if (step_cnt > 0) {
+ String reason = state.getData().suspend_reason;
+ if (!IRunControl.REASON_STEP.equals(reason)) {
+ exit(null, reason);
+ return;
+ }
+ }
+ int mode = 0;
+ if (!step_line) mode = step_back ? IRunControl.RM_REVERSE_STEP_INTO : IRunControl.RM_STEP_INTO;
+ else mode = step_back ? IRunControl.RM_REVERSE_STEP_INTO_LINE : IRunControl.RM_STEP_INTO_LINE;
+ if (ctx.canResume(mode)) {
+ if (step_cnt > 0) {
+ exit(null);
+ return;
+ }
+ ctx.resume(mode, 1, new IRunControl.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ if (error != null) exit(error);
+ }
+ });
+ step_cnt++;
+ return;
+ }
+ if (!step_line) {
+ exit(new Exception("Step into is not supported"));
+ return;
+ }
+ TCFDataCache<?> stack_trace = getStackTrace();
+ if (!stack_trace.validate(this)) return;
+ if (stack_trace.getData() == null) {
+ exit(stack_trace.getError());
+ return;
+ }
+ if (source_ref == null) {
+ TCFDataCache<TCFSourceRef> line_info = getLineInfo();
+ if (!line_info.validate(this)) return;
+ TCFSourceRef ref = line_info.getData();
+ if (ref == null) {
+ step_line = false;
+ Protocol.invokeLater(this);
+ return;
+ }
+ if (ref.error != null) {
+ exit(ref.error);
+ return;
+ }
+ setSourceRef(ref);
+ }
+ BigInteger pc = new BigInteger(state.getData().suspend_pc);
+ if (step_cnt > 0) {
+ if (pc == null || pc0 == null || pc1 == null) {
+ exit(null);
+ return;
+ }
+ if (pc.compareTo(pc0) < 0 || pc.compareTo(pc1) >= 0) {
+ TCFDataCache<TCFSourceRef> line_info = getLineInfo();
+ if (!line_info.validate(this)) return;
+ TCFSourceRef ref = line_info.getData();
+ if (ref == null || ref.area == null) {
+ exit(null);
+ }
+ else if (isSameLine(source_ref.area, ref.area)) {
+ setSourceRef(ref);
+ }
+ else if (step_back && !second_step_back) {
+ // After step back we stop at last instruction of previous line.
+ // Do second step back into line to skip that line.
+ second_step_back = true;
+ setSourceRef(ref);
+ }
+ else if (step_back && !final_step) {
+ // After second step back we have stepped one instruction more then needed.
+ // Do final step forward to correct that.
+ final_step = true;
+ step_back = false;
+ setSourceRef(ref);
+ }
+ else {
+ exit(null);
+ return;
+ }
+ }
+ }
+ step_cnt++;
+ mode = step_back ? IRunControl.RM_REVERSE_STEP_INTO_RANGE : IRunControl.RM_STEP_INTO_RANGE;
+ if (ctx.canResume(mode) &&
+ pc != null && pc0 != null && pc1 != null &&
+ pc.compareTo(pc0) >= 0 && pc.compareTo(pc1) < 0) {
+ HashMap<String,Object> args = new HashMap<String,Object>();
+ args.put(IRunControl.RP_RANGE_START, pc0);
+ args.put(IRunControl.RP_RANGE_END, pc1);
+ ctx.resume(mode, 1, args, new IRunControl.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ if (error != null) exit(error);
+ }
+ });
+ return;
+ }
+ mode = step_back ? IRunControl.RM_REVERSE_STEP_INTO : IRunControl.RM_STEP_INTO;
+ if (ctx.canResume(mode)) {
+ ctx.resume(mode, 1, new IRunControl.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ if (error != null) exit(error);
+ }
+ });
+ return;
+ }
+ 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) {
+ exit(error, "Step Into");
+ }
+
+ protected void exit(Throwable error, String reason) {
+ if (exited) return;
+ rc.removeListener(this);
+ exited = true;
+ if (error == null) setActionResult(getContextID(), reason);
+ else launch.removeContextActions(getContextID());
+ 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;
+ Protocol.invokeLater(this);
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/actions/TCFActionStepOut.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/actions/TCFActionStepOut.java
new file mode 100644
index 000000000..3656395cc
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/actions/TCFActionStepOut.java
@@ -0,0 +1,216 @@
+/*******************************************************************************
+ * Copyright (c) 2010 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 java.math.BigInteger;
+import java.util.Collection;
+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.JSON;
+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 final boolean step_back;
+ 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, boolean step_back) {
+ super(launch, ctx.getID());
+ this.ctx = ctx;
+ this.step_back = step_back;
+ }
+
+ 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;
+ try {
+ runAction();
+ }
+ catch (Throwable x) {
+ exit(x);
+ }
+ }
+
+ private void runAction() {
+ if (aborted) {
+ exit(null);
+ return;
+ }
+ if (state == null) {
+ rc.addListener(this);
+ state = getContextState();
+ if (state == null) {
+ exit(new Exception("Invalid context ID"));
+ return;
+ }
+ }
+ if (!state.validate(this)) return;
+ if (state.getData() == null || !state.getData().is_suspended) {
+ Throwable error = state.getError();
+ if (error == null) error = new Exception("Context is not suspended");
+ exit(error);
+ return;
+ }
+ TCFDataCache<?> stack_trace = getStackTrace();
+ if (!stack_trace.validate(this)) return;
+ int frame_index = getStackFrameIndex();
+ if (step_cnt > 0) {
+ TCFContextState state_data = state.getData();
+ boolean ok = isMyBreakpoint(state_data) || IRunControl.REASON_STEP.equals(state_data.suspend_reason);
+ if (!ok) exit(null, state_data.suspend_reason);
+ else if (frame_index < 0) exit(null);
+ if (exited) return;
+ }
+ int mode = step_back ? IRunControl.RM_REVERSE_STEP_OUT : IRunControl.RM_STEP_OUT;
+ if (ctx.canResume(mode)) {
+ int cnt = 1;
+ if (ctx.canCount(mode)) cnt += frame_index;
+ ctx.resume(mode, cnt, new IRunControl.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ if (error != null) exit(error);
+ }
+ });
+ step_cnt++;
+ return;
+ }
+ if (bps != null && ctx.canResume(step_back ? IRunControl.RM_REVERSE_RESUME : IRunControl.RM_RESUME)) {
+ if (bp == null) {
+ TCFDataCache<IStackTrace.StackTraceContext> frame = getStackFrame();
+ if (!frame.validate(this)) return;
+ Number addr = null;
+ if (frame.getData() != null) addr = frame.getData().getReturnAddress();
+ if (addr == null) {
+ exit(new Exception("Unknown stack frame return address"));
+ return;
+ }
+ if (step_back) {
+ BigInteger n = JSON.toBigInteger(addr);
+ addr = n.subtract(BigInteger.valueOf(1));
+ }
+ String id = "Step." + ctx.getID();
+ bp = new HashMap<String,Object>();
+ bp.put(IBreakpoints.PROP_ID, id);
+ 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(step_back ? IRunControl.RM_REVERSE_RESUME : IRunControl.RM_RESUME, 1, new IRunControl.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ if (error != null) exit(error);
+ }
+ });
+ step_cnt++;
+ return;
+ }
+ exit(new Exception("Step out is not supported"));
+ }
+
+ protected void exit(Throwable error) {
+ exit(error, "Step Out");
+ }
+
+ protected void exit(Throwable error, String reason) {
+ 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) setActionResult(getContextID(), reason);
+ else launch.removeContextActions(getContextID());
+ 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;
+ Protocol.invokeLater(this);
+ }
+
+ private boolean isMyBreakpoint(TCFContextState state_data) {
+ if (bp == null) return false;
+ if (!IRunControl.REASON_BREAKPOINT.equals(state_data.suspend_reason)) return false;
+ if (state_data.suspend_params != null) {
+ Object ids = state_data.suspend_params.get(IRunControl.STATE_BREAKPOINT_IDS);
+ if (ids != null) {
+ @SuppressWarnings("unchecked")
+ Collection<String> c = (Collection<String>)ids;
+ if (c.contains(bp.get(IBreakpoints.PROP_ID))) return true;
+ }
+ }
+ if (state_data.suspend_pc == null) return false;
+ BigInteger x = new BigInteger(state_data.suspend_pc);
+ BigInteger y = new BigInteger((String)bp.get(IBreakpoints.PROP_LOCATION));
+ return x.equals(y);
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/actions/TCFActionStepOver.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/actions/TCFActionStepOver.java
new file mode 100644
index 000000000..c52ae5834
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/actions/TCFActionStepOver.java
@@ -0,0 +1,368 @@
+/*******************************************************************************
+ * Copyright (c) 2010 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 java.math.BigInteger;
+import java.util.Collection;
+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.JSON;
+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 boolean step_line;
+ private boolean step_back;
+ 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;
+ private boolean second_step_back;
+ private boolean final_step;
+
+ protected boolean exited;
+
+ public TCFActionStepOver(TCFLaunch launch, IRunControl.RunControlContext ctx, boolean step_line, boolean step_back) {
+ super(launch, ctx.getID());
+ this.ctx = ctx;
+ this.step_line = step_line;
+ this.step_back = step_back;
+ }
+
+ 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;
+ try {
+ runAction();
+ }
+ catch (Throwable x) {
+ exit(x);
+ }
+ }
+
+ private void setSourceRef(TCFSourceRef ref) {
+ ILineNumbers.CodeArea area = ref.area;
+ if (area != null) {
+ pc0 = JSON.toBigInteger(area.start_address);
+ pc1 = JSON.toBigInteger(area.end_address);
+ }
+ else {
+ pc0 = null;
+ pc1 = null;
+ }
+ source_ref = ref;
+ }
+
+ private void runAction() {
+ if (aborted) {
+ exit(null);
+ return;
+ }
+ if (state == null) {
+ rc.addListener(this);
+ state = getContextState();
+ if (state == null) {
+ exit(new Exception("Invalid context ID"));
+ return;
+ }
+ }
+ if (!state.validate(this)) return;
+ if (state.getData() == null || !state.getData().is_suspended) {
+ Throwable error = state.getError();
+ if (error == null) error = new Exception("Context is not suspended");
+ exit(error);
+ return;
+ }
+ if (step_cnt > 0) {
+ boolean ok = false;
+ TCFContextState state_data = state.getData();
+ if (IRunControl.REASON_STEP.equals(state_data.suspend_reason) || isMyBreakpoint(state_data)) {
+ ok = true;
+ }
+ else if (IRunControl.REASON_BREAKPOINT.equals(state_data.suspend_reason) && pc0 != null && pc1 != null) {
+ BigInteger x = new BigInteger(state_data.suspend_pc);
+ ok = x.compareTo(pc0) >= 0 && x.compareTo(pc1) < 0;
+ }
+ if (!ok) {
+ exit(null, state_data.suspend_reason);
+ return;
+ }
+ }
+ int mode = 0;
+ if (!step_line) mode = step_back ? IRunControl.RM_REVERSE_STEP_OVER : IRunControl.RM_STEP_OVER;
+ else mode = step_back ? IRunControl.RM_REVERSE_STEP_OVER_LINE : IRunControl.RM_STEP_OVER_LINE;
+ if (ctx.canResume(mode)) {
+ if (step_cnt > 0) {
+ exit(null);
+ return;
+ }
+ ctx.resume(mode, 1, new IRunControl.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ if (error != null) exit(error);
+ }
+ });
+ step_cnt++;
+ return;
+ }
+ TCFDataCache<?> stack_trace = getStackTrace();
+ if (!stack_trace.validate(this)) return;
+ if (step_line && source_ref == null) {
+ line_info = getLineInfo();
+ if (!line_info.validate(this)) return;
+ TCFSourceRef ref = line_info.getData();
+ if (ref == null) {
+ step_line = false;
+ Protocol.invokeLater(this);
+ return;
+ }
+ if (ref.error != null) {
+ exit(ref.error);
+ return;
+ }
+ setSourceRef(ref);
+ }
+ int fno = getStackFrameIndex();
+ if (fno > 0) {
+ mode = step_back ? IRunControl.RM_REVERSE_STEP_OUT : IRunControl.RM_STEP_OUT;
+ if (ctx.canResume(mode)) {
+ ctx.resume(mode, 1, new IRunControl.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ if (error != null) exit(error);
+ }
+ });
+ return;
+ }
+ mode = step_back ? IRunControl.RM_REVERSE_RESUME : IRunControl.RM_RESUME;
+ if (bps != null && ctx.canResume(mode)) {
+ if (bp == null) {
+ TCFDataCache<IStackTrace.StackTraceContext> frame = getStackFrame();
+ if (!frame.validate(this)) return;
+ Number addr = frame.getData().getInstructionAddress();
+ if (addr == null) {
+ exit(new Exception("Unknown PC address"));
+ return;
+ }
+ if (step_back) {
+ BigInteger n = JSON.toBigInteger(addr);
+ addr = n.subtract(BigInteger.valueOf(1));
+ }
+ String id = "Step." + ctx.getID();
+ bp = new HashMap<String,Object>();
+ bp.put(IBreakpoints.PROP_ID, id);
+ 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(mode, 1, new IRunControl.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ if (error != null) exit(error);
+ }
+ });
+ step_cnt++;
+ return;
+ }
+ 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;
+ }
+ BigInteger pc = new BigInteger(state.getData().suspend_pc);
+ if (step_cnt > 0) {
+ if (pc0 == null && pc1 == null || state.getData().suspend_pc == null) {
+ exit(null);
+ return;
+ }
+ assert step_line;
+ if (pc.compareTo(pc0) < 0 || pc.compareTo(pc1) >= 0) {
+ if (!line_info.validate(this)) return;
+ TCFSourceRef ref = line_info.getData();
+ if (ref == null || ref.area == null) {
+ if (fno < 0 && (stack_trace.getError() == null || step_cnt >= 10)) {
+ exit(stack_trace.getError());
+ return;
+ }
+ // No line info for current PC, continue stepping
+ }
+ else if (isSameLine(source_ref.area, ref.area)) {
+ setSourceRef(ref);
+ }
+ else if (step_back && !second_step_back) {
+ // After step back we stop at last instruction of previous line.
+ // Do second step back over line to skip that line.
+ second_step_back = true;
+ setSourceRef(ref);
+ }
+ else if (step_back && !final_step) {
+ // After second step back we have stepped one instruction more then needed.
+ // Do final step forward to correct that.
+ final_step = true;
+ step_back = false;
+ setSourceRef(ref);
+ }
+ else {
+ exit(null);
+ return;
+ }
+ }
+ }
+ step_cnt++;
+ mode = step_back ? IRunControl.RM_REVERSE_STEP_OVER : IRunControl.RM_STEP_OVER;
+ if (ctx.canResume(mode)) {
+ ctx.resume(mode, 1, new IRunControl.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ if (error != null) exit(error);
+ }
+ });
+ return;
+ }
+ mode = step_back ? IRunControl.RM_REVERSE_STEP_INTO_RANGE : IRunControl.RM_STEP_INTO_RANGE;
+ if (ctx.canResume(mode) &&
+ pc != null && pc0 != null && pc1 != null &&
+ pc.compareTo(pc0) >= 0 && pc.compareTo(pc1) < 0) {
+ HashMap<String,Object> args = new HashMap<String,Object>();
+ args.put(IRunControl.RP_RANGE_START, pc0);
+ args.put(IRunControl.RP_RANGE_END, pc1);
+ ctx.resume(mode, 1, args, new IRunControl.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ if (error != null) exit(error);
+ }
+ });
+ return;
+ }
+ mode = step_back ? IRunControl.RM_REVERSE_STEP_INTO : IRunControl.RM_STEP_INTO;
+ if (ctx.canResume(mode)) {
+ ctx.resume(mode, 1, new IRunControl.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ if (error != null) exit(error);
+ }
+ });
+ return;
+ }
+ exit(new Exception("Step over is not supported"));
+ }
+
+ protected void exit(Throwable error) {
+ exit(error, "Step Over");
+ }
+
+ protected void exit(Throwable error, String reason) {
+ 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) setActionResult(getContextID(), reason);
+ else launch.removeContextActions(getContextID());
+ 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;
+ Protocol.invokeLater(this);
+ }
+
+ 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(TCFContextState state_data) {
+ if (bp == null) return false;
+ if (!IRunControl.REASON_BREAKPOINT.equals(state_data.suspend_reason)) return false;
+ if (state_data.suspend_params != null) {
+ Object ids = state_data.suspend_params.get(IRunControl.STATE_BREAKPOINT_IDS);
+ if (ids != null) {
+ @SuppressWarnings("unchecked")
+ Collection<String> c = (Collection<String>)ids;
+ if (c.contains(bp.get(IBreakpoints.PROP_ID))) return true;
+ }
+ }
+ if (state_data.suspend_pc == null) return false;
+ BigInteger x = new BigInteger(state_data.suspend_pc);
+ BigInteger y = new BigInteger((String)bp.get(IBreakpoints.PROP_LOCATION));
+ return x.equals(y);
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFLaunchDelegate.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFLaunchDelegate.java
new file mode 100644
index 000000000..ecf35cb11
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFLaunchDelegate.java
@@ -0,0 +1,350 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 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.launch;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.model.LaunchConfigurationDelegate;
+import org.eclipse.tm.internal.tcf.debug.Activator;
+import org.eclipse.tm.internal.tcf.debug.model.ITCFConstants;
+import org.eclipse.tm.internal.tcf.debug.model.TCFLaunch;
+import org.eclipse.tm.internal.tcf.debug.model.TCFMemoryRegion;
+import org.eclipse.tm.tcf.protocol.JSON;
+import org.eclipse.tm.tcf.protocol.Protocol;
+import org.eclipse.tm.tcf.services.IMemoryMap;
+import org.eclipse.tm.tcf.services.IPathMap;
+import org.eclipse.tm.tcf.util.TCFTask;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+
+public class TCFLaunchDelegate extends LaunchConfigurationDelegate {
+
+ public static final String
+ ATTR_PEER_ID = ITCFConstants.ID_TCF_DEBUG_MODEL + ".PeerID",
+ ATTR_PROJECT_NAME = ITCFConstants.ID_TCF_DEBUG_MODEL + ".ProjectName",
+ ATTR_LOCAL_PROGRAM_FILE = ITCFConstants.ID_TCF_DEBUG_MODEL + ".LocalProgramFile",
+ ATTR_REMOTE_PROGRAM_FILE = ITCFConstants.ID_TCF_DEBUG_MODEL + ".ProgramFile",
+ ATTR_COPY_TO_REMOTE_FILE = ITCFConstants.ID_TCF_DEBUG_MODEL + ".CopyToRemote",
+ ATTR_PROGRAM_ARGUMENTS = ITCFConstants.ID_TCF_DEBUG_MODEL + ".ProgramArguments",
+ ATTR_WORKING_DIRECTORY = ITCFConstants.ID_TCF_DEBUG_MODEL + ".WorkingDirectory",
+ ATTR_ATTACH_CHILDREN = ITCFConstants.ID_TCF_DEBUG_MODEL + ".AttachChildren",
+ ATTR_DISCONNECT_ON_CTX_EXIT = ITCFConstants.ID_TCF_DEBUG_MODEL + ".DisconnectOnCtxExit",
+ ATTR_USE_TERMINAL = ITCFConstants.ID_TCF_DEBUG_MODEL + ".UseTerminal",
+ ATTR_RUN_LOCAL_AGENT = ITCFConstants.ID_TCF_DEBUG_MODEL + ".RunLocalAgent",
+ ATTR_USE_LOCAL_AGENT = ITCFConstants.ID_TCF_DEBUG_MODEL + ".UseLocalAgent",
+ ATTR_SIGNALS_DONT_STOP = ITCFConstants.ID_TCF_DEBUG_MODEL + ".SignalsDontStop",
+ ATTR_SIGNALS_DONT_PASS = ITCFConstants.ID_TCF_DEBUG_MODEL + ".SignalsDontPath",
+ ATTR_PATH_MAP = ITCFConstants.ID_TCF_DEBUG_MODEL + ".PathMap",
+ ATTR_MEMORY_MAP = ITCFConstants.ID_TCF_DEBUG_MODEL + ".MemoryMap";
+
+ public static class PathMapRule implements IPathMap.PathMapRule {
+
+ private final Map<String,Object> props;
+
+ public PathMapRule(Map<String,Object> props) {
+ this.props = props;
+ }
+
+ public Map<String,Object> getProperties() {
+ return props;
+ }
+
+ public String getID() {
+ return (String)props.get(IPathMap.PROP_ID);
+ }
+
+ public String getSource() {
+ return (String)props.get(IPathMap.PROP_SOURCE);
+ }
+
+ public String getDestination() {
+ return (String)props.get(IPathMap.PROP_DESTINATION);
+ }
+
+ public String getHost() {
+ return (String)props.get(IPathMap.PROP_HOST);
+ }
+
+ public String getProtocol() {
+ return (String)props.get(IPathMap.PROP_PROTOCOL);
+ }
+
+ public String toString() {
+ StringBuffer bf = new StringBuffer();
+ for (String nm : props.keySet()) {
+ Object o = props.get(nm);
+ if (o != null) {
+ bf.append(nm);
+ bf.append('=');
+ String s = o.toString();
+ for (int i = 0; i < s.length(); i++) {
+ char ch = s.charAt(i);
+ if (ch >= ' ' && ch != '|' && ch != '\\') {
+ bf.append(ch);
+ }
+ else {
+ bf.append('\\');
+ bf.append((int)ch);
+ bf.append(';');
+ }
+ }
+ bf.append('|');
+ }
+ }
+ bf.append('|');
+ return bf.toString();
+ }
+ }
+
+ /**
+ * Given value of ATTR_PATH_MAP, return array of PathMapRule objects.
+ * @param s - value of ATTR_PATH_MAP.
+ * @return array of PathMapRule objects.
+ */
+ public static ArrayList<PathMapRule> parsePathMapAttribute(String s) {
+ ArrayList<PathMapRule> map = new ArrayList<PathMapRule>();
+ StringBuffer bf = new StringBuffer();
+ int i = 0;
+ while (i < s.length()) {
+ PathMapRule e = new PathMapRule(new HashMap<String,Object>());
+ while (i < s.length()) {
+ char ch = s.charAt(i++);
+ if (ch == '|') {
+ map.add(e);
+ break;
+ }
+ bf.setLength(0);
+ bf.append(ch);
+ while (i < s.length()) {
+ ch = s.charAt(i++);
+ if (ch == '=') break;
+ bf.append(ch);
+ }
+ String nm = bf.toString();
+ bf.setLength(0);
+ while (i < s.length()) {
+ ch = s.charAt(i++);
+ if (ch == '|') {
+ if (bf.length() > 0) e.props.put(nm, bf.toString());
+ break;
+ }
+ else if (ch == '\\') {
+ int n = 0;
+ while (i < s.length()) {
+ char d = s.charAt(i++);
+ if (d == ';') break;
+ n = n * 10 + (d - '0');
+ }
+ bf.append((char)n);
+ }
+ else {
+ bf.append(ch);
+ }
+ }
+ }
+ }
+ return map;
+ }
+
+ /**
+ * Given value of ILaunchConfiguration.ATTR_SOURCE_LOCATOR_MEMENTO,
+ * return array of PathMapRule objects.
+ * @param s - value of ATTR_PATH_MAP.
+ * @return array of PathMapRule objects.
+ */
+ public static ArrayList<PathMapRule> parseSourceLocatorMemento(String s) throws CoreException {
+ ArrayList<PathMapRule> map = new ArrayList<PathMapRule>();
+ if (s == null || s.length() == 0) return map;
+ Element root = DebugPlugin.parseDocument(s);
+ NodeList list = root.getChildNodes();
+ int length = list.getLength();
+ for (int i = 0; i < length; i++) {
+ Node node = list.item(i);
+ short type = node.getNodeType();
+ if (type == Node.ELEMENT_NODE) {
+ Element entry = (Element)node;
+ if (entry.getNodeName().equalsIgnoreCase("sourceContainers")) {
+ parseSourceContainers(map, entry);
+ }
+ }
+ }
+ return map;
+ }
+
+ private static void parseSourceContainers(ArrayList<PathMapRule> map, Element element) throws CoreException {
+ NodeList list = element.getChildNodes();
+ int length = list.getLength();
+ for (int i = 0; i < length; i++) {
+ Node node = list.item(i);
+ short type = node.getNodeType();
+ if (type == Node.ELEMENT_NODE) {
+ Element entry = (Element)node;
+ String memento = entry.getAttribute("memento");
+ if (memento != null && memento.length() > 0) readSourceContainer(map, memento);
+ }
+ }
+ }
+
+ private static void readSourceContainer(ArrayList<PathMapRule> map, String s) throws CoreException {
+ Element root = DebugPlugin.parseDocument(s);
+ if ("mapping".equals(root.getNodeName())) {
+ NodeList list = root.getChildNodes();
+ int length = list.getLength();
+ for (int i = 0; i < length; i++) {
+ Node node = list.item(i);
+ short type = node.getNodeType();
+ if (type == Node.ELEMENT_NODE) {
+ Element entry = (Element)node;
+ if (entry.getNodeName().equalsIgnoreCase("mapEntry")) {
+ String memento = entry.getAttribute("memento");
+ if (memento != null && memento.length() > 0) {
+ Element map_entry = DebugPlugin.parseDocument(memento);
+ String src = map_entry.getAttribute("backendPath");
+ String dst = map_entry.getAttribute("localPath");
+ if (src != null) src = src.replace('\\', '/');
+ PathMapRule e = new PathMapRule(new HashMap<String,Object>());
+ e.props.put(IPathMap.PROP_SOURCE, src);
+ e.props.put(IPathMap.PROP_DESTINATION, dst);
+ map.add(e);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Given value of ATTR_MEMORY_MAP, add lists of TCFMemoryRegion objects into 'maps'.
+ * @param maps - Map object to fill with memory maps.
+ * @param s - value of ATTR_MEMORY_MAP.
+ */
+ @SuppressWarnings("unchecked")
+ public static void parseMemMapsAttribute(Map<String,ArrayList<IMemoryMap.MemoryRegion>> maps, String s) throws Exception {
+ if (s == null || s.length() == 0) return;
+ Collection<Map<String,Object>> list = (Collection<Map<String,Object>>)JSON.parseOne(s.getBytes("UTF-8"));
+ if (list == null) return;
+ for (Map<String,Object> map : list) {
+ String id = (String)map.get(IMemoryMap.PROP_ID);
+ if (id != null) {
+ ArrayList<IMemoryMap.MemoryRegion> l = maps.get(id);
+ if (l == null) {
+ l = new ArrayList<IMemoryMap.MemoryRegion>();
+ maps.put(id, l);
+ }
+ l.add(new TCFMemoryRegion(map));
+ }
+ }
+ }
+
+ /**
+ * Read ATTR_MEMORY_MAP attribute of a launch configuration.
+ * @param maps - Map object to fill with memory maps.
+ * @param cfg - the launch configuration.
+ * @throws Exception
+ */
+ public static void getMemMapsAttribute(Map<String,ArrayList<IMemoryMap.MemoryRegion>> maps,
+ ILaunchConfiguration cfg) throws Exception {
+ String maps_cfg = cfg.getAttribute(ATTR_MEMORY_MAP, (String)null);
+ parseMemMapsAttribute(maps, maps_cfg);
+ }
+
+ /**
+ * Given project name and program name returns absolute path of the program.
+ * @param project_name - workspace project name.
+ * @param program_name - launch program name.
+ * @return program path or null if both project name and program name are null.
+ */
+ public static String getProgramPath(String project_name, String program_name) {
+ if (program_name == null || program_name.length() == 0) return null;
+ if (project_name == null || project_name.length() == 0) {
+ File file = new File(program_name);
+ if (!file.isAbsolute()) {
+ File ws = ResourcesPlugin.getWorkspace().getRoot().getLocation().toFile();
+ file = new File(ws, program_name);
+ }
+ return file.getAbsolutePath();
+ }
+ IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(project_name);
+ IPath program_path = new Path(program_name);
+ if (!program_path.isAbsolute()) {
+ if (project == null || !project.getFile(program_name).exists()) return null;
+ program_path = project.getFile(program_name).getLocation();
+ }
+ return program_path.toOSString();
+ }
+
+ /**
+ * Create new TCF launch object.
+ * @return new TCFLaunch object
+ */
+ public ILaunch getLaunch(final ILaunchConfiguration configuration, final String mode) throws CoreException {
+ return new TCFTask<ILaunch>() {
+ int cnt;
+ public void run() {
+ // Need to delay at least one dispatch cycle to work around
+ // a possible racing between thread that calls getLaunch() and
+ // the process of activation of other TCF plug-ins.
+ if (cnt++ < 2) Protocol.invokeLater(this);
+ else done(new TCFLaunch(configuration, mode));
+ }
+ }.getE();
+ }
+
+ /**
+ * Launch TCF session.
+ */
+ public void launch(final ILaunchConfiguration configuration, final String mode,
+ final ILaunch launch, final IProgressMonitor monitor) throws CoreException {
+ String local_id = null;
+ int task_cnt = 1;
+ if (configuration.getAttribute(ATTR_RUN_LOCAL_AGENT, false)) {
+ task_cnt++;
+ if (monitor != null) monitor.beginTask("Starting TCF Agent", task_cnt); //$NON-NLS-1$
+ local_id = TCFLocalAgent.runLocalAgent();
+ }
+ else if (configuration.getAttribute(ATTR_USE_LOCAL_AGENT, true)) {
+ task_cnt++;
+ if (monitor != null) monitor.beginTask("Searching TCF Agent", task_cnt); //$NON-NLS-1$
+ local_id = TCFLocalAgent.getLocalAgentID();
+ if (local_id == null) throw new CoreException(new Status(IStatus.ERROR,
+ Activator.PLUGIN_ID, 0,
+ "Cannot find TCF agent on the local host",
+ null));
+ }
+ if (monitor != null) monitor.beginTask("Launching TCF debugger session", task_cnt); //$NON-NLS-1$
+ final String id =
+ configuration.getAttribute(ATTR_USE_LOCAL_AGENT, true) ?
+ local_id : configuration.getAttribute(ATTR_PEER_ID, "");
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ ((TCFLaunch)launch).launchTCF(mode, id);
+ if (monitor != null) monitor.done();
+ }
+ });
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFLocalAgent.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFLocalAgent.java
new file mode 100644
index 000000000..b16fdb255
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFLocalAgent.java
@@ -0,0 +1,223 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 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.launch;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.net.URLConnection;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.tm.internal.tcf.debug.Activator;
+import org.eclipse.tm.tcf.protocol.IPeer;
+import org.eclipse.tm.tcf.protocol.Protocol;
+import org.eclipse.tm.tcf.services.ILocator;
+import org.eclipse.tm.tcf.util.TCFTask;
+import org.osgi.framework.Bundle;
+
+/**
+ * This class checks that TCF Agent is running on the local host,
+ * and starts a new instance of the agent if it cannot be located.
+ */
+public class TCFLocalAgent {
+
+ private static final String
+ AGENT_HOST = "127.0.0.1",
+ AGENT_PORT = "1534";
+
+ private static Process agent;
+ private static boolean destroed;
+
+ private static String getAgentFileName() {
+ String os = System.getProperty("os.name");
+ String arch = System.getProperty("os.arch");
+ String fnm = "agent";
+ if (arch.equals("x86")) arch = "i386";
+ if (arch.equals("i686")) arch = "i386";
+ if (os.startsWith("Windows")) {
+ os = "Windows";
+ fnm = "agent.exe";
+ }
+ if (os.equals("Linux")) os = "GNU/Linux";
+ return "agent/" + os + "/" + arch + "/" + fnm;
+ }
+
+ public static synchronized String runLocalAgent() throws CoreException {
+ if (destroed) return null;
+ String id = getLocalAgentID();
+ if (id != null) return id;
+ if (agent != null) {
+ agent.destroy();
+ agent = null;
+ }
+ Path fnm = new Path(getAgentFileName());
+ try {
+ Bundle bundle = Platform.getBundle(Activator.PLUGIN_ID);
+ URL url = FileLocator.find(bundle, fnm, null);
+ if (url != null) {
+ URLConnection ucn = url.openConnection();
+ ucn.setRequestProperty("Method", "HEAD");
+ ucn.connect();
+ long mtime = ucn.getLastModified();
+ File f = Activator.getDefault().getStateLocation().append(fnm).toFile();
+ if (!f.exists() || mtime != f.lastModified()) {
+ f.getParentFile().mkdirs();
+ InputStream inp = url.openStream();
+ OutputStream out = new FileOutputStream(f);
+ byte[] buf = new byte[0x1000];
+ for (;;) {
+ int len = inp.read(buf);
+ if (len < 0) break;
+ out.write(buf, 0, len);
+ }
+ out.close();
+ inp.close();
+ if (!"exe".equals(fnm.getFileExtension())) {
+ String[] cmd = {
+ "chmod",
+ "a+x",
+ f.getAbsolutePath()
+ };
+ Runtime.getRuntime().exec(cmd).waitFor();
+ }
+ f.setLastModified(mtime);
+ }
+ String[] cmd = {
+ f.getAbsolutePath(),
+ "-s",
+ "TCP:" + AGENT_HOST + ":" + AGENT_PORT
+ };
+ final Process prs = agent = Runtime.getRuntime().exec(cmd);
+ final TCFTask<String> waiting = waitAgentReady();
+ Thread t = new Thread() {
+ public void run() {
+ try {
+ final int n = prs.waitFor();
+ if (n != 0) {
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ if (waiting.isDone()) return;
+ waiting.error(new IOException("TCF Agent exited with code " + n));
+ }
+ });
+ }
+ synchronized (TCFLocalAgent.class) {
+ if (agent == prs) {
+ if (n != 0 && !destroed) {
+ Activator.log("TCF Agent exited with code " + n, null);
+ }
+ agent = null;
+ }
+ }
+ }
+ catch (InterruptedException x) {
+ Activator.log("TCF Agent Monitor interrupted", x);
+ }
+ }
+ };
+ t.setDaemon(true);
+ t.setName("TCF Agent Monitor");
+ t.start();
+ return waiting.getIO();
+ }
+ }
+ catch (Throwable x) {
+ agent = null;
+ throw new CoreException(new Status(IStatus.ERROR,
+ Activator.PLUGIN_ID, 0,
+ "Cannot start local TCF agent.",
+ x));
+ }
+ throw new CoreException(new Status(IStatus.ERROR,
+ Activator.PLUGIN_ID, 0,
+ "Cannot start local TCF agent: file not available:\n" + fnm,
+ null));
+ }
+
+ private static boolean isLocalAgent(IPeer p) {
+ String prot = p.getTransportName();
+ if (prot.equals("PIPE")) return true;
+ if (prot.equals("UNIX")) {
+ String port = p.getAttributes().get(IPeer.ATTR_IP_PORT);
+ return AGENT_PORT.equals(port);
+ }
+ String host = p.getAttributes().get(IPeer.ATTR_IP_HOST);
+ String port = p.getAttributes().get(IPeer.ATTR_IP_PORT);
+ return AGENT_HOST.equals(host) && AGENT_PORT.equals(port);
+ }
+
+ public static synchronized String getLocalAgentID() {
+ return new TCFTask<String>() {
+ public void run() {
+ final ILocator locator = Protocol.getLocator();
+ for (IPeer p : locator.getPeers().values()) {
+ if (isLocalAgent(p)) {
+ done(p.getID());
+ return;
+ }
+ }
+ done(null);
+ }
+ }.getE();
+ }
+
+ private static TCFTask<String> waitAgentReady() {
+ return new TCFTask<String>() {
+ public void run() {
+ final ILocator locator = Protocol.getLocator();
+ for (IPeer p : locator.getPeers().values()) {
+ if (isLocalAgent(p)) {
+ done(p.getID());
+ return;
+ }
+ }
+ final ILocator.LocatorListener listener = new ILocator.LocatorListener() {
+ public void peerAdded(IPeer p) {
+ if (!isDone() && isLocalAgent(p)) {
+ done(p.getID());
+ locator.removeListener(this);
+ }
+ }
+ public void peerChanged(IPeer peer) {
+ }
+ public void peerHeartBeat(String id) {
+ }
+ public void peerRemoved(String id) {
+ }
+ };
+ locator.addListener(listener);
+ Protocol.invokeLater(30000, new Runnable() {
+ public void run() {
+ if (!isDone()) {
+ error(new Exception("Timeout waiting for TCF Agent to start"));
+ locator.removeListener(listener);
+ }
+ }
+ });
+ }
+ };
+ }
+
+ public static synchronized void destroy() {
+ if (agent != null) {
+ destroed = true;
+ agent.destroy();
+ }
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFSourceLookupDirector.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFSourceLookupDirector.java
new file mode 100644
index 000000000..48cc08230
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFSourceLookupDirector.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 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.launch;
+
+import org.eclipse.debug.core.model.ISourceLocator;
+import org.eclipse.debug.core.model.IStackFrame;
+import org.eclipse.debug.core.sourcelookup.AbstractSourceLookupDirector;
+import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
+import org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant;
+import org.eclipse.tm.tcf.services.ILineNumbers;
+
+/**
+ * TCF source lookup director.
+ * For TCF source lookup there is one source lookup participant.
+ */
+public class TCFSourceLookupDirector extends AbstractSourceLookupDirector {
+
+ public static Object lookup(ISourceLocator locator, Object element) {
+ Object source_element = null;
+ if (locator instanceof ISourceLookupDirector) {
+ if (element instanceof ILineNumbers.CodeArea) {
+ String file_name = TCFSourceLookupParticipant.toFileName((ILineNumbers.CodeArea)element);
+ if (file_name != null) source_element = ((ISourceLookupDirector)locator).getSourceElement(file_name);
+ }
+ else {
+ source_element = ((ISourceLookupDirector)locator).getSourceElement(element);
+ }
+ }
+ else if (element instanceof IStackFrame) {
+ source_element = locator.getSourceElement((IStackFrame)element);
+ }
+ return source_element;
+ }
+
+ public void initializeParticipants() {
+ addParticipants(new ISourceLookupParticipant[] { new TCFSourceLookupParticipant() });
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFSourceLookupParticipant.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFSourceLookupParticipant.java
new file mode 100644
index 000000000..350f72a48
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFSourceLookupParticipant.java
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tm.internal.tcf.debug.launch;
+
+import java.io.File;
+import java.net.InetAddress;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.eclipse.core.filesystem.URIUtil;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.sourcelookup.AbstractSourceLookupParticipant;
+import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
+import org.eclipse.debug.core.sourcelookup.containers.LocalFileStorage;
+import org.eclipse.tm.internal.tcf.debug.launch.TCFLaunchDelegate.PathMapRule;
+import org.eclipse.tm.tcf.services.ILineNumbers;
+
+/**
+ * The TCF source lookup participant knows how to translate a ILineNumbers.CodeArea
+ * into a source file name
+ */
+public class TCFSourceLookupParticipant extends AbstractSourceLookupParticipant {
+
+ @SuppressWarnings("serial")
+ private final LinkedHashMap<String,Object[]> cache = new LinkedHashMap<String,Object[]>(511, 0.75f, true) {
+ @Override
+ @SuppressWarnings("rawtypes")
+ protected boolean removeEldestEntry(Map.Entry eldest) {
+ return size() > 1023;
+ }
+ };
+
+ @Override
+ public void sourceContainersChanged(ISourceLookupDirector director) {
+ cache.clear();
+ }
+
+ public String getSourceName(Object object) throws CoreException {
+ if (object instanceof String) {
+ return (String)object;
+ }
+ if (object instanceof ILineNumbers.CodeArea) {
+ ILineNumbers.CodeArea area = (ILineNumbers.CodeArea)object;
+ return toFileName(area);
+ }
+ return null;
+ }
+
+ public static String toFileName(ILineNumbers.CodeArea area) {
+ if (area.directory != null && area.file != null && !isAbsolutePath(area.file)) {
+ return area.directory + "/" + area.file;
+ }
+ return area.file;
+ }
+
+ private static boolean isAbsolutePath(String fnm) {
+ if (fnm.length() == 0) return false;
+ char ch = fnm.charAt(0);
+ if (ch == '/' || ch == '\\') return true;
+ if (fnm.length() >= 3 && fnm.charAt(1) == ':') {
+ ch = fnm.charAt(2);
+ if (ch == '/' || ch == '\\') return true;
+ }
+ return false;
+ }
+
+ private String applyPathMap(String fnm) {
+ ILaunchConfiguration cfg = getDirector().getLaunchConfiguration();
+ if (cfg == null) return fnm;
+ try {
+ String path_map = cfg.getAttribute(TCFLaunchDelegate.ATTR_PATH_MAP, "");
+ if (path_map.length() == 0) return fnm;
+ ArrayList<PathMapRule> map = TCFLaunchDelegate.parsePathMapAttribute(path_map);
+ for (PathMapRule r : map) {
+ String src = r.getSource();
+ if (!fnm.startsWith(src)) continue;
+ String host = r.getHost();
+ if (host != null && host.length() > 0) {
+ if (!InetAddress.getLocalHost().equals(InetAddress.getByName(host))) continue;
+ }
+ String dst = r.getDestination();
+ if (dst == null || dst.length() == 0) continue;
+ int l = src.length();
+ if (dst.endsWith("/") && l < fnm.length() && fnm.charAt(l) == '/') l++;
+ return dst + fnm.substring(l);
+ }
+ if (fnm.startsWith("/cygdrive/")) {
+ fnm = fnm.substring(10, 11) + ":" + fnm.substring(11);
+ }
+ return fnm;
+ }
+ catch (Exception x) {
+ return fnm;
+ }
+ }
+
+ private Object[] findSource(String name) throws CoreException {
+ Object[] res;
+ File file = new File(applyPathMap(name));
+ if (file.isAbsolute() && file.exists() && file.isFile()) {
+ res = new Object[]{ new LocalFileStorage(file) };
+ }
+ else {
+ res = super.findSourceElements(name);
+ }
+ ArrayList<Object> list = new ArrayList<Object>();
+ for (Object o : res) {
+ if (o instanceof IStorage && !(o instanceof IFile)) {
+ IPath path = ((IStorage)o).getFullPath();
+ if (path != null) {
+ URI uri = URIUtil.toURI(path);
+ IFile[] arr = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(uri);
+ if (arr != null && arr.length > 0) {
+ int cnt = list.size();
+ for (IFile fileResource : arr) {
+ if (fileResource.isAccessible()) {
+ list.add(fileResource);
+ }
+ }
+ if (list.size() > cnt) continue;
+ }
+ }
+ }
+ list.add(o);
+ }
+ return list.toArray(new Object[list.size()]);
+ }
+
+ @Override
+ public Object[] findSourceElements(Object object) throws CoreException {
+ String name = getSourceName(object);
+ if (name == null) return null;
+ if (cache.containsKey(name)) return cache.get(name);
+ Object[] res = findSource(name);
+ cache.put(name, res);
+ return res;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFSourcePathComputerDelegate.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFSourcePathComputerDelegate.java
new file mode 100644
index 000000000..2b5b74776
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFSourcePathComputerDelegate.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 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.launch;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashSet;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.filesystem.URIUtil;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.sourcelookup.ISourceContainer;
+import org.eclipse.debug.core.sourcelookup.ISourcePathComputerDelegate;
+import org.eclipse.debug.core.sourcelookup.containers.ProjectSourceContainer;
+import org.eclipse.debug.core.sourcelookup.containers.WorkspaceSourceContainer;
+
+/**
+ * Computes the default source lookup path for a TCF launch configuration. The
+ * default source lookup path is the project containing the TCF
+ * program being launched. If the program is not specified, the workspace is
+ * searched by default.
+ */
+public class TCFSourcePathComputerDelegate implements ISourcePathComputerDelegate {
+
+ public ISourceContainer[] computeSourceContainers(
+ ILaunchConfiguration configuration, IProgressMonitor monitor)
+ throws CoreException {
+ ArrayList<ISourceContainer> res = new ArrayList<ISourceContainer>();
+ String project_name = configuration.getAttribute(TCFLaunchDelegate.ATTR_PROJECT_NAME, (String)null);
+ String program_name = configuration.getAttribute(TCFLaunchDelegate.ATTR_LOCAL_PROGRAM_FILE, (String)null);
+ String path = TCFLaunchDelegate.getProgramPath(project_name, program_name);
+ if (path != null) {
+ URI uri = URIUtil.toURI(new Path(path));
+ IFile[] files = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(uri);
+ if (files != null && files.length > 0) {
+ HashSet<IProject> projects = new HashSet<IProject>();
+ for (IFile file : files) projects.add(file.getProject());
+ for (IProject project : projects) res.add(new ProjectSourceContainer(project, true));
+ }
+ }
+ if (res.size() == 0 && project_name != null && project_name.length() > 0) {
+ IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(project_name);
+ if (project != null) res.add(new ProjectSourceContainer(project, true));
+ }
+ if (res.size() == 0) res.add(new WorkspaceSourceContainer());
+ return res.toArray(new ISourceContainer[res.size()]);
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFUserDefPeer.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFUserDefPeer.java
new file mode 100644
index 000000000..a045fdbf4
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/launch/TCFUserDefPeer.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 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.launch;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.tm.internal.tcf.debug.Activator;
+import org.eclipse.tm.tcf.core.AbstractPeer;
+import org.eclipse.tm.tcf.protocol.IPeer;
+import org.eclipse.tm.tcf.protocol.Protocol;
+
+/**
+ * The class represents manually configured (user defined) TCF peers (targets).
+ * Unlike auto-discovered peers, manually configured ones are persistent -
+ * they exist until explicitly deleted by user.
+ * Eclipse plug-in state storage is used to keep the configuration data.
+ */
+public class TCFUserDefPeer extends AbstractPeer {
+
+ public TCFUserDefPeer(Map<String, String> attrs) {
+ super(attrs);
+ }
+
+ /**
+ * Load manually configured peers from persistent storage.
+ */
+ public static void loadPeers() {
+ try {
+ assert Protocol.isDispatchThread();
+ IPath path = Activator.getDefault().getStateLocation();
+ File f = path.append("peers.ini").toFile();
+ if (!f.exists()) return;
+ HashMap<String,String> attrs = new HashMap<String,String>();
+ BufferedReader rd = new BufferedReader(new InputStreamReader(new FileInputStream(f), "UTF-8"));
+ for (;;) {
+ String s = rd.readLine();
+ if (s == null) break;
+ if (s.length() == 0) {
+ new TCFUserDefPeer(attrs);
+ attrs = new HashMap<String,String>();
+ }
+ else {
+ int i = s.indexOf('=');
+ if (i > 0) attrs.put(s.substring(0, i), s.substring(i + 1));
+ }
+ }
+ rd.close();
+ }
+ catch (Exception x) {
+ Activator.log("Cannot read peer list", x);
+ }
+ }
+
+ /**
+ * Save manually configured peers to persistent storage.
+ */
+ public static void savePeers() {
+ try {
+ assert Protocol.isDispatchThread();
+ IPath path = Activator.getDefault().getStateLocation();
+ File f = path.append("peers.ini").toFile();
+ BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f), "UTF-8"));
+ for (IPeer peer : Protocol.getLocator().getPeers().values()) {
+ if (peer instanceof TCFUserDefPeer) {
+ Map<String,String> attrs = peer.getAttributes();
+ for (String nm : attrs.keySet()) {
+ wr.write(nm);
+ wr.write('=');
+ wr.write(attrs.get(nm));
+ wr.newLine();
+ }
+ wr.newLine();
+ }
+ }
+ wr.close();
+ }
+ catch (Exception x) {
+ Activator.log("Cannot save peer list", x);
+ }
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/ITCFBreakpointListener.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/ITCFBreakpointListener.java
new file mode 100644
index 000000000..80ce441e1
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/ITCFBreakpointListener.java
@@ -0,0 +1,19 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tm.internal.tcf.debug.model;
+
+public interface ITCFBreakpointListener {
+
+ public void breakpointStatusChanged(String id);
+ public void breakpointRemoved(String id);
+ public void breakpointChanged(String id);
+
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/ITCFConstants.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/ITCFConstants.java
new file mode 100644
index 000000000..ab5d7179a
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/ITCFConstants.java
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 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;
+
+public interface ITCFConstants {
+
+ /**
+ * Unique identifier for the TCF debug model
+ */
+ public static final String ID_TCF_DEBUG_MODEL = "org.eclipse.tm.tcf.debug";
+
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFBreakpoint.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFBreakpoint.java
new file mode 100644
index 000000000..3a3528e6b
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFBreakpoint.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 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;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.resources.WorkspaceJob;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.model.Breakpoint;
+import org.eclipse.tm.internal.tcf.debug.Activator;
+import org.eclipse.tm.tcf.protocol.Protocol;
+import org.eclipse.tm.tcf.services.IBreakpoints;
+
+
+public class TCFBreakpoint extends Breakpoint {
+
+ public static final String MARKER_TYPE = "org.eclipse.tm.tcf.debug.breakpoint.marker";
+
+ private static final String[] attr_names = {
+ TCFBreakpointsModel.ATTR_ADDRESS, "address",
+ TCFBreakpointsModel.ATTR_FUNCTION, "location",
+ TCFBreakpointsModel.ATTR_EXPRESSION, "expression",
+ TCFBreakpointsModel.ATTR_CONDITION, "condition",
+ TCFBreakpointsModel.ATTR_CONTEXTNAMES, "scope (names)",
+ TCFBreakpointsModel.ATTR_CONTEXTIDS, "scope (IDs)",
+ TCFBreakpointsModel.ATTR_EXE_PATHS, "scope (modules)",
+ TCFBreakpointsModel.ATTR_STOP_GROUP, "stop group",
+ };
+
+ private static long last_id = 0;
+
+ private static String createNewID() {
+ assert Protocol.isDispatchThread();
+ long id = System.currentTimeMillis();
+ if (id <= last_id) id = last_id + 1;
+ last_id = id;
+ return Long.toHexString(id);
+ }
+
+ public static TCFBreakpoint createFromMarkerAttributes(Map<String,Object> attrs) throws CoreException {
+ assert !Protocol.isDispatchThread();
+ assert attrs.get(TCFBreakpointsModel.ATTR_ID) != null;
+ TCFBreakpoint bp = new TCFBreakpoint();
+ IResource resource = ResourcesPlugin.getWorkspace().getRoot();
+ IMarker marker = resource.createMarker(MARKER_TYPE);
+ bp.setMarker(marker);
+ marker.setAttributes(attrs);
+ DebugPlugin.getDefault().getBreakpointManager().addBreakpoint(bp);
+ return bp;
+ }
+
+ public static TCFBreakpoint createFromTCFProperties(Map<String,Object> props) {
+ assert Protocol.isDispatchThread();
+ if (props.get(IBreakpoints.PROP_ID) == null) props.put(IBreakpoints.PROP_ID, createNewID());
+ final TCFBreakpoint bp = new TCFBreakpoint();
+ final Map<String,Object> m = Activator.getBreakpointsModel().toMarkerAttributes(props);
+ final IResource resource = ResourcesPlugin.getWorkspace().getRoot();
+ final ISchedulingRule rule = bp.getMarkerRule(resource);
+ Job job = new WorkspaceJob("Add Breakpoint") { //$NON-NLS-1$
+ @Override
+ public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException {
+ IMarker marker = resource.createMarker(MARKER_TYPE);
+ bp.setMarker(marker);
+ marker.setAttributes(m);
+ DebugPlugin.getDefault().getBreakpointManager().addBreakpoint(bp);
+ return Status.OK_STATUS;
+ }
+ };
+ job.setRule(rule);
+ job.setPriority(Job.SHORT);
+ job.setSystem(true);
+ job.schedule();
+ return bp;
+ }
+
+ public TCFBreakpoint() {
+ }
+
+ @Override
+ public void setEnabled(boolean b) throws CoreException {
+ if (!Activator.getBreakpointsModel().isLocal(getMarker())) return;
+ super.setEnabled(b);
+ }
+
+ public String getModelIdentifier() {
+ return ITCFConstants.ID_TCF_DEBUG_MODEL;
+ }
+
+ public String getText() {
+ IMarker marker = getMarker();
+ if (marker == null) return null;
+ StringBuffer bf = new StringBuffer();
+ for (int i = 0; i < attr_names.length; i += 2) {
+ String s = marker.getAttribute(attr_names[i], null);
+ if (s == null || s.length() == 0) continue;
+ bf.append('[');
+ bf.append(attr_names[i + 1]);
+ bf.append(": ");
+ bf.append(s);
+ bf.append(']');
+ }
+ if (bf.length() == 0) {
+ String id = marker.getAttribute(
+ ITCFConstants.ID_TCF_DEBUG_MODEL + '.' + IBreakpoints.PROP_ID, null);
+ bf.append(id);
+ }
+ return bf.toString();
+ }
+
+ public void notifyStatusChaged() throws CoreException {
+ IMarker marker = getMarker();
+ if (marker == null) return;
+ int cnt = 0;
+ String status = marker.getAttribute(TCFBreakpointsModel.ATTR_STATUS, null);
+ if (status != null) cnt = Integer.parseInt(status);
+ setAttribute(TCFBreakpointsModel.ATTR_STATUS, Integer.toString(cnt + 1));
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFBreakpointsModel.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFBreakpointsModel.java
new file mode 100644
index 000000000..e796716cb
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFBreakpointsModel.java
@@ -0,0 +1,676 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tm.internal.tcf.debug.model;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IMarkerDelta;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.IBreakpointListener;
+import org.eclipse.debug.core.IBreakpointManager;
+import org.eclipse.debug.core.IBreakpointManagerListener;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.tm.internal.tcf.debug.Activator;
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.protocol.Protocol;
+import org.eclipse.tm.tcf.services.IBreakpoints;
+
+/**
+ * TCFBreakpointsModel class handles breakpoints for all active TCF launches.
+ * It downloads initial set of breakpoint data when launch is activated,
+ * listens for Eclipse breakpoint manager events and propagates breakpoint changes to TCF targets.
+ */
+public class TCFBreakpointsModel {
+
+ public static final String
+ CDATA_CLIENT_ID = "ClientID",
+ CDATA_TYPE = "Type",
+ CDATA_FILE = "File",
+ CDATA_MARKER = "Marker";
+
+ public static final String
+ ATTR_ID = ITCFConstants.ID_TCF_DEBUG_MODEL + '.' + IBreakpoints.PROP_ID,
+ ATTR_STATUS = ITCFConstants.ID_TCF_DEBUG_MODEL + '.' + "Status",
+ ATTR_INSTALL_COUNT = "org.eclipse.cdt.debug.core.installCount",
+ ATTR_ADDRESS = "org.eclipse.cdt.debug.core.address",
+ ATTR_FUNCTION = "org.eclipse.cdt.debug.core.function",
+ ATTR_EXPRESSION = "org.eclipse.cdt.debug.core.expression",
+ ATTR_READ = "org.eclipse.cdt.debug.core.read",
+ ATTR_WRITE = "org.eclipse.cdt.debug.core.write",
+ ATTR_SIZE = "org.eclipse.cdt.debug.core.range",
+ ATTR_FILE = "org.eclipse.cdt.debug.core.sourceHandle",
+ ATTR_CONDITION = "org.eclipse.cdt.debug.core.condition",
+ ATTR_IGNORE_COUNT = "org.eclipse.cdt.debug.core.ignoreCount",
+ ATTR_CONTEXTNAMES = ITCFConstants.ID_TCF_DEBUG_MODEL + '.' + IBreakpoints.PROP_CONTEXTNAMES,
+ ATTR_CONTEXTIDS = ITCFConstants.ID_TCF_DEBUG_MODEL + '.' + IBreakpoints.PROP_CONTEXTIDS,
+ ATTR_EXE_PATHS = ITCFConstants.ID_TCF_DEBUG_MODEL + '.' + IBreakpoints.PROP_EXECUTABLEPATHS,
+ ATTR_STOP_GROUP = ITCFConstants.ID_TCF_DEBUG_MODEL + '.' + IBreakpoints.PROP_STOP_GROUP;
+
+ private final IBreakpointManager bp_manager = DebugPlugin.getDefault().getBreakpointManager();
+ private final HashMap<IChannel,Map<String,Object>> channels = new HashMap<IChannel,Map<String,Object>>();
+ private final HashMap<String,IBreakpoint> id2bp = new HashMap<String,IBreakpoint>();
+ private final String client_id = UUID.randomUUID().toString();
+
+ private abstract class BreakpointUpdate implements Runnable {
+
+ private final IBreakpoint breakpoint;
+ private boolean removed;
+ protected final Map<String,Object> marker_attrs;
+ protected final boolean is_local;
+ protected final String marker_id;
+ private final String marker_file;
+ private final String marker_type;
+
+ IBreakpoints service;
+ IBreakpoints.DoneCommand done;
+ Map<String,Object> tcf_attrs;
+
+ BreakpointUpdate(IBreakpoint breakpoint, boolean removed) throws CoreException, IOException {
+ this.breakpoint = breakpoint;
+ this.removed = removed;
+ IMarker marker = breakpoint.getMarker();
+ marker_attrs = new HashMap<String,Object>(marker.getAttributes());
+ is_local = isLocal(marker);
+ marker_file = getFilePath(breakpoint.getMarker().getResource());
+ marker_type = breakpoint.getMarker().getType();
+ marker_id = getBreakpointID(breakpoint);
+ }
+
+ synchronized void exec() throws InterruptedException {
+ assert !Protocol.isDispatchThread();
+ if (marker_id != null) {
+ Protocol.invokeLater(this);
+ wait();
+ }
+ }
+
+ public void run() {
+ if (disposed) return;
+ if (removed) id2bp.remove(marker_id);
+ else id2bp.put(marker_id, breakpoint);
+ if (is_local) {
+ for (final IChannel channel : channels.keySet()) {
+ tcf_attrs = toBreakpointAttributes(channel, marker_id, marker_file, marker_type, marker_attrs);
+ service = channel.getRemoteService(IBreakpoints.class);
+ if (!isSupported(channel, breakpoint)) continue;
+ done = new IBreakpoints.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ if (error != null) channel.terminate(error);
+ }
+ };
+ update();
+ }
+ }
+ Protocol.sync(new Runnable() {
+ public void run() {
+ synchronized (BreakpointUpdate.this) {
+ BreakpointUpdate.this.notify();
+ }
+ }
+ });
+ };
+
+ abstract void update();
+ }
+
+ private final IBreakpointListener breakpoint_listener = new IBreakpointListener() {
+
+ public void breakpointAdded(final IBreakpoint breakpoint) {
+ try {
+ new BreakpointUpdate(breakpoint, false) {
+ @Override
+ void update() {
+ service.add(tcf_attrs, done);
+ }
+ }.exec();
+ }
+ catch (Throwable x) {
+ Activator.log("Unhandled exception in breakpoint listener", x);
+ }
+ }
+
+ private Set<String> calcMarkerDeltaKeys(IMarker marker, IMarkerDelta delta) throws CoreException {
+ Set<String> keys = new HashSet<String>();
+ if (delta == null) return keys;
+ Map<String,Object> m0 = delta.getAttributes();
+ Map<String,Object> m1 = marker.getAttributes();
+ if (m0 != null) keys.addAll(m0.keySet());
+ if (m1 != null) keys.addAll(m1.keySet());
+ for (Iterator<String> i = keys.iterator(); i.hasNext();) {
+ String key = i.next();
+ Object v0 = m0 != null ? m0.get(key) : null;
+ Object v1 = m1 != null ? m1.get(key) : null;
+ if (v0 instanceof String && ((String)v0).length() == 0) v0 = null;
+ if (v1 instanceof String && ((String)v1).length() == 0) v1 = null;
+ if (v0 instanceof Boolean && !((Boolean)v0).booleanValue()) v0 = null;
+ if (v1 instanceof Boolean && !((Boolean)v1).booleanValue()) v1 = null;
+ if ((v0 == null) != (v1 == null)) continue;
+ if (v0 != null && !v0.equals(v1)) continue;
+ i.remove();
+ }
+ keys.remove(ATTR_INSTALL_COUNT);
+ keys.remove(ATTR_STATUS);
+ return keys;
+ }
+
+ public void breakpointChanged(final IBreakpoint breakpoint, IMarkerDelta delta) {
+ try {
+ final Set<String> s = calcMarkerDeltaKeys(breakpoint.getMarker(), delta);
+ if (s.isEmpty()) return;
+ new BreakpointUpdate(breakpoint, false) {
+ @Override
+ void update() {
+ if (s.size() == 1 && s.contains(IBreakpoint.ENABLED)) {
+ Boolean enabled = (Boolean)tcf_attrs.get(IBreakpoints.PROP_ENABLED);
+ if (enabled == null || !enabled.booleanValue()) {
+ service.disable(new String[]{ marker_id }, done);
+ }
+ else {
+ service.enable(new String[]{ marker_id }, done);
+ }
+ }
+ else {
+ service.change(tcf_attrs, done);
+ }
+ }
+ }.exec();
+ }
+ catch (Throwable x) {
+ Activator.log("Unhandled exception in breakpoint listener", x);
+ }
+ }
+
+ public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
+ try {
+ new BreakpointUpdate(breakpoint, true) {
+ @Override
+ void update() {
+ service.remove(new String[]{ marker_id }, done);
+ }
+ }.exec();
+ }
+ catch (Throwable x) {
+ Activator.log("Unhandled exception in breakpoint listener", x);
+ }
+ }
+ };
+
+ private final IBreakpointManagerListener manager_listener = new IBreakpointManagerListener() {
+
+ public void breakpointManagerEnablementChanged(final boolean enabled) {
+ try {
+ IBreakpoint[] arr = bp_manager.getBreakpoints();
+ if (arr == null || arr.length == 0) return;
+ final Map<String,IBreakpoint> map = new HashMap<String,IBreakpoint>();
+ for (int i = 0; i < arr.length; i++) {
+ IMarker marker = arr[i].getMarker();
+ Boolean b = marker.getAttribute(IBreakpoint.ENABLED, Boolean.FALSE);
+ if (!b.booleanValue()) continue;
+ String id = getBreakpointID(arr[i]);
+ if (id == null) continue;
+ map.put(id, arr[i]);
+ }
+ if (map.isEmpty()) return;
+ Runnable r = new Runnable() {
+ public void run() {
+ if (disposed) return;
+ for (final IChannel channel : channels.keySet()) {
+ IBreakpoints service = channel.getRemoteService(IBreakpoints.class);
+ Set<String> ids = new HashSet<String>();
+ for (String id : map.keySet()) {
+ IBreakpoint bp = map.get(id);
+ if (isSupported(channel, bp)) ids.add(id);
+ }
+ IBreakpoints.DoneCommand done = new IBreakpoints.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ if (error != null) channel.terminate(error);
+ }
+ };
+ if (enabled) {
+ service.enable(ids.toArray(new String[ids.size()]), done);
+ }
+ else {
+ service.disable(ids.toArray(new String[ids.size()]), done);
+ }
+ }
+ Protocol.sync(new Runnable() {
+ public void run() {
+ synchronized (map) {
+ map.notify();
+ }
+ }
+ });
+ }
+ };
+ synchronized (map) {
+ assert !Protocol.isDispatchThread();
+ Protocol.invokeLater(r);
+ map.wait();
+ }
+ }
+ catch (Throwable x) {
+ Activator.log("Unhandled exception in breakpoint listener", x);
+ }
+ }
+ };
+
+ private boolean disposed;
+
+ public static TCFBreakpointsModel getBreakpointsModel() {
+ return Activator.getBreakpointsModel();
+ }
+
+ public void dispose() {
+ bp_manager.removeBreakpointListener(breakpoint_listener);
+ bp_manager.removeBreakpointManagerListener(manager_listener);
+ channels.clear();
+ disposed = true;
+ }
+
+ public boolean isSupported(IChannel channel, IBreakpoint bp) {
+ // TODO: implement per-channel breakpoint filtering
+ return true;
+ }
+
+ /**
+ * Get TCF ID of a breakpoint.
+ * @param bp - IBreakpoint object.
+ * @return TCF ID of the breakpoint.
+ * @throws CoreException
+ */
+ public static String getBreakpointID(IBreakpoint bp) throws CoreException {
+ IMarker marker = bp.getMarker();
+ String id = (String)marker.getAttributes().get(ATTR_ID);
+ if (id != null) return id;
+ id = marker.getResource().getLocationURI().toString();
+ if (id == null) return null;
+ return id + ':' + marker.getId();
+ }
+
+ /**
+ * Get IBreakpoint for given TCF breakpoint ID.
+ * The mapping works only for breakpoints that were sent to (or received from) a debug target.
+ * It can be used to map target responses to IBreakpoint objects.
+ * @param id - TCF breakpoint ID.
+ * @return IBreakpoint object associated with the ID, or null.
+ */
+ public IBreakpoint getBreakpoint(String id) {
+ assert Protocol.isDispatchThread();
+ return id2bp.get(id);
+ }
+
+ /**
+ * Check breakpoint ownership.
+ * @param properties - breakpoint properties as reported by TCF Breakpoints service.
+ * @return true if the breakpoint is owned by local instance of Eclipse.
+ * Return false if the properties represent a breakpoint created by some other TCF client.
+ */
+ @SuppressWarnings("unchecked")
+ public boolean isLocal(Map<String,Object> properties) {
+ if (properties == null) return false;
+ Map<String,Object> client_data = (Map<String,Object>)properties.get(IBreakpoints.PROP_CLIENT_DATA);
+ if (client_data == null) return false;
+ String id = (String)client_data.get(TCFBreakpointsModel.CDATA_CLIENT_ID);
+ return client_id.equals(id);
+ }
+
+ /**
+ * Check breakpoint marker ownership.
+ * @param marker - breakpoint marker.
+ * @return true if the marker is owned by local instance of Eclipse.
+ * Return false if the marker represents a breakpoint created by some other TCF client.
+ */
+ public boolean isLocal(IMarker marker) {
+ try {
+ Map<String,Object> marker_attrs = marker.getAttributes();
+ if (marker_attrs == null) return false;
+ return !Boolean.FALSE.equals(marker_attrs.get(IBreakpoint.PERSISTED));
+ }
+ catch (CoreException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Download breakpoint info to a target.
+ * This is supposed to be done once after a channel is opened.
+ * @param channel - TCF communication channel.
+ * @param done - client callback.
+ * @throws IOException
+ * @throws CoreException
+ */
+ @SuppressWarnings("unchecked")
+ public void downloadBreakpoints(final IChannel channel, final Runnable done) throws IOException, CoreException {
+ assert !disposed;
+ assert Protocol.isDispatchThread();
+ final IBreakpoints service = channel.getRemoteService(IBreakpoints.class);
+ if (service != null) {
+ service.getCapabilities(null, new IBreakpoints.DoneGetCapabilities() {
+ public void doneGetCapabilities(IToken token, Exception error, Map<String,Object> capabilities) {
+ if (channel.getState() != IChannel.STATE_OPEN) {
+ Protocol.invokeLater(done);
+ return;
+ }
+ if (channels.isEmpty()) {
+ bp_manager.addBreakpointListener(breakpoint_listener);
+ bp_manager.addBreakpointManagerListener(manager_listener);
+ }
+ channel.addChannelListener(new IChannel.IChannelListener() {
+ public void congestionLevel(int level) {
+ }
+ public void onChannelClosed(Throwable error) {
+ if (disposed) return;
+ channels.remove(channel);
+ if (channels.isEmpty()) {
+ bp_manager.removeBreakpointListener(breakpoint_listener);
+ bp_manager.removeBreakpointManagerListener(manager_listener);
+ id2bp.clear();
+ }
+ }
+ public void onChannelOpened() {
+ }
+ });
+ channels.put(channel, capabilities);
+ IBreakpoint[] arr = bp_manager.getBreakpoints();
+ if (arr != null && arr.length > 0) {
+ List<Map<String,Object>> bps = new ArrayList<Map<String,Object>>(arr.length);
+ for (int i = 0; i < arr.length; i++) {
+ try {
+ if (!isSupported(channel, arr[i])) continue;
+ String id = getBreakpointID(arr[i]);
+ if (id == null) continue;
+ if (!arr[i].isPersisted()) continue;
+ IMarker marker = arr[i].getMarker();
+ String file = getFilePath(marker.getResource());
+ bps.add(toBreakpointAttributes(channel, id, file, marker.getType(), marker.getAttributes()));
+ id2bp.put(id, arr[i]);
+ }
+ catch (Exception x) {
+ Activator.log("Cannot get breakpoint attributes", x);
+ }
+ }
+ if (!bps.isEmpty()) {
+ Map<String, Object>[] bp_arr = (Map<String,Object>[])bps.toArray(new Map[bps.size()]);
+ service.set(bp_arr, new IBreakpoints.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ if (error == null) done.run();
+ else channel.terminate(error);
+ }
+ });
+ return;
+ }
+ }
+ Protocol.invokeLater(done);
+ }
+ });
+ }
+ }
+
+ private String getFilePath(IResource resource) throws IOException {
+ if (resource == ResourcesPlugin.getWorkspace().getRoot()) return null;
+ IPath p = resource.getRawLocation();
+ if (p == null) return null;
+ return p.toFile().getCanonicalPath();
+ }
+
+ /**
+ * Translate TCF breakpoint properties to Eclipse breakpoint marker attributes.
+ * @param p - TCF breakpoint properties.
+ * @return Eclipse marker attributes.
+ */
+ @SuppressWarnings("unchecked")
+ public Map<String,Object> toMarkerAttributes(Map<String,Object> p) {
+ assert !disposed;
+ assert Protocol.isDispatchThread();
+ Map<String,Object> client_data = (Map<String,Object>)p.get(IBreakpoints.PROP_CLIENT_DATA);
+ if (client_data != null) {
+ Map<String,Object> m = (Map<String,Object>)client_data.get(CDATA_MARKER);
+ if (m != null) return m;
+ }
+ Map<String,Object> m = new HashMap<String,Object>();
+ for (Map.Entry<String,Object> e : p.entrySet()) {
+ String key = e.getKey();
+ Object val = e.getValue();
+ if (key.equals(IBreakpoints.PROP_ENABLED)) continue;
+ if (key.equals(IBreakpoints.PROP_FILE)) continue;
+ if (key.equals(IBreakpoints.PROP_LINE)) continue;
+ if (key.equals(IBreakpoints.PROP_COLUMN)) continue;
+ if (key.equals(IBreakpoints.PROP_LOCATION)) continue;
+ if (key.equals(IBreakpoints.PROP_ACCESSMODE)) continue;
+ if (key.equals(IBreakpoints.PROP_SIZE)) continue;
+ if (key.equals(IBreakpoints.PROP_CONDITION)) continue;
+ if (val instanceof String[]) {
+ StringBuffer bf = new StringBuffer();
+ for (String s : (String[])val) {
+ if (bf.length() > 0) bf.append(',');
+ bf.append(s);
+ }
+ if (bf.length() == 0) continue;
+ val = bf.toString();
+ }
+ else if (val instanceof Collection) {
+ StringBuffer bf = new StringBuffer();
+ for (String s : (Collection<String>)val) {
+ if (bf.length() > 0) bf.append(',');
+ bf.append(s);
+ }
+ if (bf.length() == 0) continue;
+ val = bf.toString();
+ }
+ m.put(ITCFConstants.ID_TCF_DEBUG_MODEL + '.' + key, val);
+ }
+ Boolean enabled = (Boolean)p.get(IBreakpoints.PROP_ENABLED);
+ if (enabled == null) m.put(IBreakpoint.ENABLED, Boolean.FALSE);
+ else m.put(IBreakpoint.ENABLED, enabled);
+ String location = (String)p.get(IBreakpoints.PROP_LOCATION);
+ if (location != null && location.length() > 0) {
+ int access_mode = IBreakpoints.ACCESSMODE_EXECUTE;
+ Number access_mode_num = (Number)p.get(IBreakpoints.PROP_ACCESSMODE);
+ if (access_mode_num != null) access_mode = access_mode_num.intValue();
+ if ((access_mode & IBreakpoints.ACCESSMODE_EXECUTE) != 0) {
+ if (Character.isDigit(location.charAt(0))) {
+ m.put(ATTR_ADDRESS, location);
+ }
+ else {
+ m.put(ATTR_FUNCTION, location);
+ }
+ }
+ else {
+ m.put(ATTR_EXPRESSION, location.replaceFirst("^&\\((.+)\\)$", "$1"));
+ m.put(ATTR_READ, (access_mode & IBreakpoints.ACCESSMODE_READ) != 0);
+ m.put(ATTR_WRITE, (access_mode & IBreakpoints.ACCESSMODE_WRITE) != 0);
+ }
+ Number size_num = (Number)p.get(IBreakpoints.PROP_SIZE);
+ if (size_num != null) m.put(ATTR_SIZE, size_num.toString());
+ }
+ m.put(IBreakpoint.REGISTERED, Boolean.TRUE);
+ m.put(IBreakpoint.PERSISTED, Boolean.TRUE);
+ m.put(IBreakpoint.ID, ITCFConstants.ID_TCF_DEBUG_MODEL);
+ String msg = "";
+ if (location != null) msg += location;
+ m.put(IMarker.MESSAGE, "Breakpoint: " + msg);
+ String file = (String)p.get(IBreakpoints.PROP_FILE);
+ if (file != null && file.length() > 0) {
+ m.put(ATTR_FILE, file);
+ }
+ Number line = (Number)p.get(IBreakpoints.PROP_LINE);
+ if (line != null) {
+ m.put(IMarker.LINE_NUMBER, new Integer(line.intValue()));
+ Number column = (Number)p.get(IBreakpoints.PROP_COLUMN);
+ if (column != null) {
+ m.put(IMarker.CHAR_START, new Integer(column.intValue()));
+ m.put(IMarker.CHAR_END, new Integer(column.intValue() + 1));
+ }
+ }
+ String condition = (String)p.get(IBreakpoints.PROP_CONDITION);
+ if (condition != null && condition.length() > 0) m.put(ATTR_CONDITION, condition);
+ Number ignore_count = (Number)p.get(IBreakpoints.PROP_IGNORECOUNT);
+ if (ignore_count != null) m.put(ATTR_IGNORE_COUNT, ignore_count);
+ return m;
+ }
+
+ /**
+ * Translate Eclipse breakpoint marker attributes to TCF breakpoint properties.
+ * @param channel - TCF communication channel.
+ * @param id - breakpoint ID.
+ * @param file - the maker file or null.
+ * @param type - the marker type.
+ * @param p - the marker attributes.
+ * @return TCF breakpoint properties.
+ */
+ public Map<String,Object> toBreakpointAttributes(IChannel channel, String id, String file, String type, Map<String,Object> p) {
+ assert !disposed;
+ assert Protocol.isDispatchThread();
+ Map<String,Object> m = new HashMap<String,Object>();
+ Map<String,Object> capabilities = channels.get(channel);
+ Map<String,Object> client_data = null;
+ if (capabilities != null) {
+ Object obj = capabilities.get(IBreakpoints.CAPABILITY_CLIENT_DATA);
+ if (obj instanceof Boolean && ((Boolean)obj).booleanValue()) client_data = new HashMap<String,Object>();
+ }
+ m.put(IBreakpoints.PROP_ID, id);
+ if (client_data != null) {
+ m.put(IBreakpoints.PROP_CLIENT_DATA, client_data);
+ client_data.put(CDATA_CLIENT_ID, client_id);
+ if (type != null) client_data.put(CDATA_TYPE, type);
+ if (file != null) client_data.put(CDATA_FILE, file);
+ client_data.put(CDATA_MARKER, p);
+ }
+ for (Map.Entry<String,Object> e : p.entrySet()) {
+ String key = e.getKey();
+ Object val = e.getValue();
+ if (key.startsWith(ITCFConstants.ID_TCF_DEBUG_MODEL)) {
+ String tcf_key = key.substring(ITCFConstants.ID_TCF_DEBUG_MODEL.length() + 1);
+ if (IBreakpoints.PROP_CONTEXTIDS.equals(tcf_key)) {
+ val = filterContextIds(channel, ((String)val).split(",\\s*"));
+ }
+ else if (IBreakpoints.PROP_CONTEXTNAMES.equals(tcf_key) ||
+ IBreakpoints.PROP_STOP_GROUP.equals(tcf_key) ||
+ IBreakpoints.PROP_EXECUTABLEPATHS.equals(tcf_key)) {
+ val = ((String)val).split(",\\s*");
+ }
+ m.put(tcf_key, val);
+ }
+ }
+ Boolean enabled = (Boolean)p.get(IBreakpoint.ENABLED);
+ if (enabled != null && enabled.booleanValue() && bp_manager.isEnabled()) {
+ m.put(IBreakpoints.PROP_ENABLED, enabled);
+ }
+ if (file == null) file = (String)p.get(ATTR_FILE);
+ if (file != null && file.length() > 0) {
+ String name = file;
+ boolean file_mapping = false;
+ if (capabilities != null) {
+ Object obj = capabilities.get(IBreakpoints.CAPABILITY_FILE_MAPPING);
+ if (obj instanceof Boolean) file_mapping = ((Boolean)obj).booleanValue();
+ }
+ if (!file_mapping) {
+ int i = file.lastIndexOf('/');
+ int j = file.lastIndexOf('\\');
+ if (i > j) name = file.substring(i + 1);
+ else if (i < j) name = file.substring(j + 1);
+ }
+ m.put(IBreakpoints.PROP_FILE, name);
+ Integer line = (Integer)p.get(IMarker.LINE_NUMBER);
+ if (line != null) {
+ m.put(IBreakpoints.PROP_LINE, new Integer(line.intValue()));
+ Integer column = (Integer)p.get(IMarker.CHAR_START);
+ if (column != null) m.put(IBreakpoints.PROP_COLUMN, column);
+ }
+ }
+ if (p.get(ATTR_EXPRESSION) != null) {
+ String expr = (String)p.get(ATTR_EXPRESSION);
+ if (expr != null && expr.length() != 0) {
+ boolean writeAccess = Boolean.TRUE.equals(p.get(ATTR_WRITE));
+ boolean readAccess = Boolean.TRUE.equals(p.get(ATTR_READ));
+ int accessMode = 0;
+ if (readAccess) accessMode |= IBreakpoints.ACCESSMODE_READ;
+ if (writeAccess) accessMode |= IBreakpoints.ACCESSMODE_WRITE;
+ m.put(IBreakpoints.PROP_ACCESSMODE, Integer.valueOf(accessMode));
+ Object range = p.get(ATTR_SIZE);
+ if (range != null) {
+ int size = Integer.parseInt(range.toString());
+ if (size > 0) m.put(IBreakpoints.PROP_SIZE, size);
+ }
+ if (!Character.isDigit(expr.charAt(0))) {
+ expr = "&(" + expr + ')';
+ }
+ m.put(IBreakpoints.PROP_LOCATION, expr);
+ }
+ }
+ else if (p.get(ATTR_FUNCTION) != null) {
+ String expr = (String)p.get(ATTR_FUNCTION);
+ if (expr != null && expr.length() != 0) m.put(IBreakpoints.PROP_LOCATION, expr);
+ }
+ else if (file == null) {
+ String address = (String)p.get(ATTR_ADDRESS);
+ if (address != null && address.length() > 0) m.put(IBreakpoints.PROP_LOCATION, address);
+ }
+ String condition = (String)p.get(ATTR_CONDITION);
+ if (condition != null && condition.length() > 0) m.put(IBreakpoints.PROP_CONDITION, condition);
+ Number ignore_count = (Number)p.get(ATTR_IGNORE_COUNT);
+ if (ignore_count != null && ignore_count.intValue() > 0) m.put(IBreakpoints.PROP_IGNORECOUNT, ignore_count);
+ return m;
+ }
+
+ /**
+ * Filter given array of scope IDs of the form sessionId/contextId
+ * to those applicable to the given channel.
+ */
+ private String[] filterContextIds(IChannel channel, String[] scopeIds) {
+ String sessionId = getSessionId(channel);
+ List<String> contextIds = new ArrayList<String>();
+ for (String scopeId : scopeIds) {
+ if (scopeId.length() == 0) continue;
+ int slash = scopeId.indexOf('/');
+ if (slash < 0) {
+ contextIds.add(scopeId);
+ }
+ else if (sessionId != null && sessionId.equals(scopeId.substring(0, slash))) {
+ contextIds.add(scopeId.substring(slash+1));
+ }
+ }
+ return (String[]) contextIds.toArray(new String[contextIds.size()]);
+ }
+
+ /**
+ * @return launch config name for given channel or <code>null</code>
+ */
+ private String getSessionId(IChannel channel) {
+ ILaunch[] launches = DebugPlugin.getDefault().getLaunchManager().getLaunches();
+ for (ILaunch launch : launches) {
+ if (launch instanceof TCFLaunch) {
+ if (channel == ((TCFLaunch) launch).getChannel()) {
+ ILaunchConfiguration lc = launch.getLaunchConfiguration();
+ return lc != null ? lc.getName() : null;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFBreakpointsStatus.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFBreakpointsStatus.java
new file mode 100644
index 000000000..f65428522
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFBreakpointsStatus.java
@@ -0,0 +1,151 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tm.internal.tcf.debug.model;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.protocol.Protocol;
+import org.eclipse.tm.tcf.services.IBreakpoints;
+
+
+public class TCFBreakpointsStatus {
+
+ private final IBreakpoints service;
+ private final Map<String,Map<String,Object>> breakpoints = new HashMap<String,Map<String,Object>>();
+ private final Map<String,Map<String,Object>> status = new HashMap<String,Map<String,Object>>();
+ private final Set<ITCFBreakpointListener> listeners = new HashSet<ITCFBreakpointListener>();
+
+ private static final Map<String,Object> status_not_supported = new HashMap<String,Object>();
+
+ static {
+ status_not_supported.put(IBreakpoints.STATUS_ERROR, "Not supported");
+ }
+
+ TCFBreakpointsStatus(TCFLaunch launch) {
+ assert Protocol.isDispatchThread();
+ service = launch.getChannel().getRemoteService(IBreakpoints.class);
+ if (service != null) {
+ final IBreakpoints.BreakpointsListener listener = new IBreakpoints.BreakpointsListener() {
+
+ public void breakpointStatusChanged(String id, Map<String,Object> m) {
+ assert Protocol.isDispatchThread();
+ if (status.get(id) == null) return;
+ status.put(id, m);
+ for (Iterator<ITCFBreakpointListener> i = listeners.iterator(); i.hasNext();) {
+ i.next().breakpointStatusChanged(id);
+ }
+ }
+
+ public void contextAdded(Map<String,Object>[] bps) {
+ for (Map<String,Object> bp : bps) {
+ String id = (String)bp.get(IBreakpoints.PROP_ID);
+ breakpoints.put(id, bp);
+ if (status.get(id) != null) continue;
+ status.put(id, new HashMap<String,Object>());
+ for (Iterator<ITCFBreakpointListener> i = listeners.iterator(); i.hasNext();) {
+ i.next().breakpointStatusChanged(id);
+ }
+ }
+ }
+
+ public void contextChanged(Map<String,Object>[] bps) {
+ for (Map<String,Object> bp : bps) {
+ String id = (String)bp.get(IBreakpoints.PROP_ID);
+ breakpoints.put(id, bp);
+ if (!status.containsKey(id)) continue;
+ for (Iterator<ITCFBreakpointListener> i = listeners.iterator(); i.hasNext();) {
+ i.next().breakpointChanged(id);
+ }
+ }
+ }
+
+ public void contextRemoved(String[] ids) {
+ for (String id : ids) {
+ breakpoints.remove(id);
+ if (!status.containsKey(id)) continue;
+ for (Iterator<ITCFBreakpointListener> i = listeners.iterator(); i.hasNext();) {
+ i.next().breakpointRemoved(id);
+ }
+ status.remove(id);
+ }
+ }
+ };
+ service.addListener(listener);
+
+ // query foreign breakpoints
+ service.getIDs(new IBreakpoints.DoneGetIDs() {
+ @SuppressWarnings("unchecked")
+ public void doneGetIDs(IToken token, Exception error, String[] ids) {
+ if (error != null || ids == null) return;
+ for (final String id : ids) {
+ service.getProperties(id, new IBreakpoints.DoneGetProperties() {
+ public void doneGetProperties(IToken token, Exception error, Map<String,Object> props) {
+ if (error == null) {
+ listener.contextAdded((Map<String,Object>[]) new Map[] { props });
+ service.getStatus(id, new IBreakpoints.DoneGetStatus() {
+ public void doneGetStatus(IToken token, Exception error, Map<String,Object> status) {
+ if (error == null) listener.breakpointStatusChanged(id, status);
+ }
+ });
+ }
+ }
+ });
+ }
+ }
+ });
+ }
+ }
+
+ public Set<String> getStatusIDs() {
+ return status.keySet();
+ }
+
+ public Map<String,Object> getStatus(String id) {
+ assert id != null;
+ assert Protocol.isDispatchThread();
+ if (service == null) return status_not_supported;
+ return status.get(id);
+ }
+
+ public Map<String,Object> getProperties(String id) {
+ assert id != null;
+ assert Protocol.isDispatchThread();
+ return breakpoints.get(id);
+ }
+
+ public Map<String,Object> getStatus(IBreakpoint bp) {
+ try {
+ String id = TCFBreakpointsModel.getBreakpointID(bp);
+ if (id == null) return status_not_supported;
+ return getStatus(id);
+ }
+ catch (CoreException e) {
+ return status_not_supported;
+ }
+ }
+
+ public void addListener(ITCFBreakpointListener listener) {
+ assert Protocol.isDispatchThread();
+ listeners.add(listener);
+ }
+
+ public void removeListener(ITCFBreakpointListener listener) {
+ assert Protocol.isDispatchThread();
+ listeners.remove(listener);
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFContextState.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFContextState.java
new file mode 100644
index 000000000..62eca91b2
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFContextState.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 String suspend_pc;
+ public String suspend_reason;
+ public Map<String,Object> suspend_params;
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFError.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFError.java
new file mode 100644
index 000000000..967b658ac
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFError.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 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 org.eclipse.core.runtime.IStatus;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.tm.internal.tcf.debug.Activator;
+
+
+public class TCFError extends DebugException {
+
+ private static final long serialVersionUID = -4261097789666829020L;
+
+ public TCFError(Throwable exception) {
+ super(new Status(exception));
+ }
+
+ private static class Status implements IStatus {
+
+ private final Throwable exception;
+
+ private Status(Throwable exception) {
+ this.exception = exception;
+ }
+
+ public IStatus[] getChildren() {
+ return null;
+ }
+
+ public int getCode() {
+ return 1;
+ }
+
+ public Throwable getException() {
+ return exception;
+ }
+
+ public String getMessage() {
+ return exception.getMessage();
+ }
+
+ public String getPlugin() {
+ return Activator.PLUGIN_ID;
+ }
+
+ public int getSeverity() {
+ return ERROR;
+ }
+
+ public boolean isMultiStatus() {
+ return false;
+ }
+
+ public boolean isOK() {
+ return false;
+ }
+
+ public boolean matches(int severityMask) {
+ return false;
+ }
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFFunctionRef.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFFunctionRef.java
new file mode 100644
index 000000000..e76b721d3
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFFunctionRef.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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.math.BigInteger;
+
+/**
+ * Objects of this class represent a mapping between an address and a function.
+ */
+public class TCFFunctionRef {
+ public String context_id;
+ public int address_size;
+ public BigInteger address;
+ public String symbol_id;
+ public Throwable error;
+
+ public String toString() {
+ StringBuffer bf = new StringBuffer();
+ bf.append('[');
+ bf.append(context_id);
+ bf.append(',');
+ bf.append(address_size);
+ bf.append(',');
+ bf.append(address);
+ bf.append(',');
+ bf.append(symbol_id);
+ bf.append(',');
+ bf.append(error);
+ bf.append(']');
+ return bf.toString();
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFLaunch.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFLaunch.java
new file mode 100644
index 000000000..885b8b18f
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFLaunch.java
@@ -0,0 +1,1318 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tm.internal.tcf.debug.model;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.DebugPlugin;
+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.internal.tcf.debug.launch.TCFLaunchDelegate.PathMapRule;
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IPeer;
+import org.eclipse.tm.tcf.protocol.IService;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.protocol.Protocol;
+import org.eclipse.tm.tcf.services.IFileSystem;
+import org.eclipse.tm.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tm.tcf.services.IFileSystem.IFileHandle;
+import org.eclipse.tm.tcf.services.IMemory;
+import org.eclipse.tm.tcf.services.IMemory.MemoryContext;
+import org.eclipse.tm.tcf.services.IMemoryMap;
+import org.eclipse.tm.tcf.services.IPathMap;
+import org.eclipse.tm.tcf.services.IProcesses;
+import org.eclipse.tm.tcf.services.IProcesses.ProcessContext;
+import org.eclipse.tm.tcf.services.IProcessesV1;
+import org.eclipse.tm.tcf.services.IRunControl;
+import org.eclipse.tm.tcf.services.IStreams;
+import org.eclipse.tm.tcf.util.TCFTask;
+
+public class TCFLaunch extends Launch {
+
+ public interface LaunchListener {
+
+ public void onCreated(TCFLaunch launch);
+
+ public void onConnected(TCFLaunch launch);
+
+ public void onDisconnected(TCFLaunch launch);
+
+ public void onProcessOutput(TCFLaunch launch, String process_id, int stream_id, byte[] data);
+
+ public void onProcessStreamError(
+ TCFLaunch launch, String process_id, int stream_id,
+ Exception error, int lost_size);
+ }
+
+ public interface ActionsListener {
+
+ public void onContextActionStart(TCFAction action);
+
+ public void onContextActionResult(String id, String result);
+
+ public void onContextActionDone(TCFAction action);
+ }
+
+ private abstract class LaunchStep implements Runnable {
+
+ LaunchStep() {
+ launch_steps.add(this);
+ }
+
+ abstract void start() throws Exception;
+
+ void done() {
+ if (channel.getState() != IChannel.STATE_OPEN) return;
+ try {
+ launch_steps.removeFirst().start();
+ }
+ catch (Throwable x) {
+ channel.terminate(x);
+ }
+ }
+
+ public void run() {
+ done();
+ }
+ }
+
+ private static final Collection<LaunchListener> listeners = new ArrayList<LaunchListener>();
+ private static LaunchListener[] listeners_array;
+
+ private final Collection<ActionsListener> action_listeners = new ArrayList<ActionsListener>();
+
+ private IChannel channel;
+ private Throwable error;
+ private TCFBreakpointsStatus breakpoints_status;
+ private String mode;
+ private boolean connecting;
+ private boolean disconnecting;
+ private boolean disconnected;
+ private boolean shutdown;
+ private boolean last_context_exited;
+ private long actions_interval;
+
+ private final HashSet<Object> pending_clients = new HashSet<Object>();
+ private long pending_clients_timestamp;
+
+ private String peer_name;
+
+ private Runnable update_memory_maps;
+
+ private ProcessContext process;
+ private Collection<Map<String,Object>> process_signals;
+ private IToken process_start_command;
+ private String process_input_stream_id;
+ private boolean process_exited;
+ private int process_exit_code;
+ private final HashMap<String,String> process_env = new HashMap<String,String>();
+
+ private final HashMap<String,TCFAction> active_actions = new HashMap<String,TCFAction>();
+ private final HashMap<String,LinkedList<TCFAction>> context_action_queue = new HashMap<String,LinkedList<TCFAction>>();
+ private final HashMap<String,Long> context_action_timestamps = new HashMap<String,Long>();
+ private final HashMap<String,String> stream_ids = new HashMap<String,String>();
+ private final LinkedList<LaunchStep> launch_steps = new LinkedList<LaunchStep>();
+ private final LinkedList<String> redirection_path = new LinkedList<String>();
+
+ private ArrayList<PathMapRule> filepath_map;
+
+ private Set<String> context_filter;
+
+ private boolean supports_memory_map_preloading;
+
+ private final IStreams.StreamsListener streams_listener = new IStreams.StreamsListener() {
+
+ public void created(String stream_type, String stream_id, String context_id) {
+ stream_ids.put(stream_id, context_id);
+ if (process_start_command == null) {
+ disconnectStream(stream_id);
+ }
+ }
+
+ public void disposed(String stream_type, String stream_id) {
+ }
+ };
+
+ private final IProcesses.ProcessesListener prs_listener = new IProcesses.ProcessesListener() {
+
+ public void exited(String process_id, int exit_code) {
+ if (process_id.equals(process.getID())) {
+ process_exit_code = exit_code;
+ process_exited = true;
+ }
+ }
+ };
+
+ private static LaunchListener[] getListeners() {
+ if (listeners_array != null) return listeners_array;
+ return listeners_array = listeners.toArray(new LaunchListener[listeners.size()]);
+ }
+
+ public TCFLaunch(ILaunchConfiguration launchConfiguration, String mode) {
+ super(launchConfiguration, mode, null);
+ for (LaunchListener l : getListeners()) l.onCreated(TCFLaunch.this);
+ }
+
+ private void onConnected() throws Exception {
+ // The method is called when TCF channel is successfully connected.
+
+ final ILaunchConfiguration cfg = getLaunchConfiguration();
+ if (cfg != null) {
+ // Send file path map:
+ if (getService(IPathMap.class) != null) {
+ new LaunchStep() {
+ @Override
+ void start() throws Exception {
+ downloadPathMaps(cfg, this);
+ }
+ };
+ }
+ }
+
+ if (redirection_path.size() > 0) {
+ // Connected to intermediate peer (value-add).
+ // Redirect to next peer:
+ new LaunchStep() {
+ @Override
+ void start() throws Exception {
+ channel.redirect(redirection_path.removeFirst());
+ }
+ };
+ }
+ else {
+ final IStreams streams = getService(IStreams.class);
+ if (streams != null) {
+ // Subscribe Streams service:
+ new LaunchStep() {
+ @Override
+ void start() {
+ final Set<IToken> cmds = new HashSet<IToken>();
+ String[] nms = { IProcesses.NAME, IProcessesV1.NAME };
+ for (String s : nms) {
+ if (channel.getRemoteService(s) == null) continue;
+ cmds.add(streams.subscribe(s, streams_listener, new IStreams.DoneSubscribe() {
+ public void doneSubscribe(IToken token, Exception error) {
+ cmds.remove(token);
+ if (error != null) channel.terminate(error);
+ if (cmds.size() == 0) done();
+ }
+ }));
+ }
+ if (cmds.size() == 0) done();
+ }
+ };
+ }
+
+ if (mode.equals(ILaunchManager.DEBUG_MODE)) {
+ String attach_to_context = getAttribute("attach_to_context");
+ if (attach_to_context != null) {
+ context_filter = new HashSet<String>();
+ context_filter.add(attach_to_context);
+ }
+ final IMemoryMap mem_map = channel.getRemoteService(IMemoryMap.class);
+ if (mem_map != null) {
+ // Send manual memory map items:
+ new LaunchStep() {
+ @Override
+ void start() throws Exception {
+ final Runnable done = this;
+ // Check if preloading is supported
+ mem_map.set("\001", null, new IMemoryMap.DoneSet() {
+ public void doneSet(IToken token, Exception error) {
+ try {
+ supports_memory_map_preloading = error == null;
+ if (!supports_memory_map_preloading) {
+ // Older agents (up to ver. 0.4) don't support preloading of memory maps.
+ updateMemoryMapsOnProcessCreation(cfg, done);
+ }
+ else {
+ downloadMemoryMaps(cfg, done);
+ }
+ }
+ catch (Exception x) {
+ channel.terminate(x);
+ }
+ }
+ });
+ }
+ };
+ }
+ // Send breakpoints:
+ new LaunchStep() {
+ @Override
+ void start() throws Exception {
+ breakpoints_status = new TCFBreakpointsStatus(TCFLaunch.this);
+ Activator.getBreakpointsModel().downloadBreakpoints(channel, this);
+ }
+ };
+ }
+
+ // Call client launch sequence:
+ new LaunchStep() {
+ @Override
+ void start() {
+ runLaunchSequence(this);
+ }
+ };
+
+ if (cfg != null) startRemoteProcess(cfg);
+
+ // Final launch step.
+ // Notify clients:
+ new LaunchStep() {
+ @Override
+ void start() {
+ connecting = false;
+ for (LaunchListener l : getListeners()) l.onConnected(TCFLaunch.this);
+ fireChanged();
+ }
+ };
+ }
+
+ launch_steps.removeFirst().start();
+ }
+
+ private void onDisconnected(Throwable error) {
+ // The method is called when TCF channel is closed.
+ assert !disconnected;
+ assert !shutdown;
+ this.error = error;
+ breakpoints_status = null;
+ connecting = false;
+ disconnected = true;
+ for (LaunchListener l : getListeners()) l.onDisconnected(this);
+ if (DebugPlugin.getDefault() != null) fireChanged();
+ runShutdownSequence(new Runnable() {
+ public void run() {
+ shutdown = true;
+ if (DebugPlugin.getDefault() != null) fireTerminate();
+ }
+ });
+ }
+
+ protected void runLaunchSequence(Runnable done) {
+ done.run();
+ }
+
+ private void downloadMemoryMaps(final ILaunchConfiguration cfg, final Runnable done) throws Exception {
+ final IMemoryMap mmap = channel.getRemoteService(IMemoryMap.class);
+ if (mmap == null) {
+ done.run();
+ return;
+ }
+ final HashMap<String,ArrayList<IMemoryMap.MemoryRegion>> maps = new HashMap<String,ArrayList<IMemoryMap.MemoryRegion>>();
+ TCFLaunchDelegate.getMemMapsAttribute(maps, cfg);
+ final HashSet<IToken> cmds = new HashSet<IToken>(); // Pending commands
+ final Runnable done_all = new Runnable() {
+ boolean launch_done;
+ public void run() {
+ if (launch_done) return;
+ done.run();
+ launch_done = true;
+ }
+ };
+ final IMemoryMap.DoneSet done_set_mmap = new IMemoryMap.DoneSet() {
+ public void doneSet(IToken token, Exception error) {
+ assert cmds.contains(token);
+ cmds.remove(token);
+ if (error != null) Activator.log("Cannot update context memory map", error);
+ if (cmds.isEmpty()) done_all.run();
+ }
+ };
+ for (String id : maps.keySet()) {
+ ArrayList<IMemoryMap.MemoryRegion> map = maps.get(id);
+ TCFMemoryRegion[] arr = map.toArray(new TCFMemoryRegion[map.size()]);
+ cmds.add(mmap.set(id, arr, done_set_mmap));
+ }
+ update_memory_maps = new Runnable() {
+ public void run() {
+ try {
+ Set<String> set = new HashSet<String>(maps.keySet());
+ maps.clear();
+ TCFLaunchDelegate.getMemMapsAttribute(maps, cfg);
+ for (String id : maps.keySet()) {
+ ArrayList<IMemoryMap.MemoryRegion> map = maps.get(id);
+ TCFMemoryRegion[] arr = map.toArray(new TCFMemoryRegion[map.size()]);
+ cmds.add(mmap.set(id, arr, done_set_mmap));
+ }
+ for (String id : set) {
+ if (maps.get(id) != null) continue;
+ cmds.add(mmap.set(id, null, done_set_mmap));
+ }
+ }
+ catch (Throwable x) {
+ channel.terminate(x);
+ }
+ }
+ };
+ if (cmds.isEmpty()) done_all.run();
+ }
+
+ private void updateMemoryMapsOnProcessCreation(ILaunchConfiguration cfg, final Runnable done) throws Exception {
+ final IMemory mem = channel.getRemoteService(IMemory.class);
+ final IMemoryMap mmap = channel.getRemoteService(IMemoryMap.class);
+ if (mem == null || mmap == null) {
+ done.run();
+ return;
+ }
+ final HashSet<String> deleted_maps = new HashSet<String>();
+ final HashMap<String,ArrayList<IMemoryMap.MemoryRegion>> maps = new HashMap<String,ArrayList<IMemoryMap.MemoryRegion>>();
+ TCFLaunchDelegate.getMemMapsAttribute(maps, cfg);
+ final HashSet<String> mems = new HashSet<String>(); // Already processed memory IDs
+ final HashSet<IToken> cmds = new HashSet<IToken>(); // Pending commands
+ final HashMap<String,String> mem2map = new HashMap<String,String>();
+ final Runnable done_all = new Runnable() {
+ boolean launch_done;
+ public void run() {
+ mems.clear();
+ deleted_maps.clear();
+ if (launch_done) return;
+ done.run();
+ launch_done = true;
+ }
+ };
+ final IMemoryMap.DoneSet done_set_mmap = new IMemoryMap.DoneSet() {
+ public void doneSet(IToken token, Exception error) {
+ cmds.remove(token);
+ if (error != null) Activator.log("Cannot update context memory map", error);
+ if (cmds.isEmpty()) done_all.run();
+ }
+ };
+ final IMemory.DoneGetContext done_get_context = new IMemory.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, MemoryContext context) {
+ cmds.remove(token);
+ if (context != null && mems.add(context.getID())) {
+ String id = context.getName();
+ if (id == null) id = context.getID();
+ if (id != null) {
+ ArrayList<IMemoryMap.MemoryRegion> map = maps.get(id);
+ if (map != null) {
+ TCFMemoryRegion[] arr = map.toArray(new TCFMemoryRegion[map.size()]);
+ cmds.add(mmap.set(context.getID(), arr, done_set_mmap));
+ mem2map.put(context.getID(), id);
+ }
+ else if (deleted_maps.contains(id)) {
+ cmds.add(mmap.set(context.getID(), null, done_set_mmap));
+ mem2map.remove(context.getID());
+ }
+ }
+ }
+ if (cmds.isEmpty()) done_all.run();
+ }
+ };
+ final IMemory.DoneGetChildren done_get_children = new IMemory.DoneGetChildren() {
+ public void doneGetChildren(IToken token, Exception error, String[] ids) {
+ cmds.remove(token);
+ if (ids != null) {
+ for (String id : ids) {
+ cmds.add(mem.getChildren(id, this));
+ cmds.add(mem.getContext(id, done_get_context));
+ }
+ }
+ if (cmds.isEmpty()) done_all.run();
+ }
+ };
+ cmds.add(mem.getChildren(null, done_get_children));
+ mem.addListener(new IMemory.MemoryListener() {
+ public void memoryChanged(String context_id, Number[] addr, long[] size) {
+ }
+ public void contextRemoved(String[] context_ids) {
+ for (String id : context_ids) {
+ mems.remove(id);
+ mem2map.remove(id);
+ }
+ }
+ public void contextChanged(MemoryContext[] contexts) {
+ for (MemoryContext context : contexts) {
+ String id = context.getName();
+ if (id == null) id = context.getID();
+ if (id == null) continue;
+ if (id.equals(mem2map.get(context.getID()))) continue;
+ ArrayList<IMemoryMap.MemoryRegion> map = maps.get(id);
+ if (map == null) continue;
+ TCFMemoryRegion[] arr = map.toArray(new TCFMemoryRegion[map.size()]);
+ cmds.add(mmap.set(context.getID(), arr, done_set_mmap));
+ mem2map.put(context.getID(), id);
+ }
+ }
+ public void contextAdded(MemoryContext[] contexts) {
+ for (MemoryContext context : contexts) {
+ if (!mems.add(context.getID())) continue;
+ String id = context.getName();
+ if (id == null) id = context.getID();
+ if (id == null) continue;
+ ArrayList<IMemoryMap.MemoryRegion> map = maps.get(id);
+ if (map == null) continue;
+ TCFMemoryRegion[] arr = map.toArray(new TCFMemoryRegion[map.size()]);
+ cmds.add(mmap.set(context.getID(), arr, done_set_mmap));
+ mem2map.put(context.getID(), id);
+ }
+ }
+ });
+ update_memory_maps = new Runnable() {
+ public void run() {
+ try {
+ maps.clear();
+ mems.clear();
+ TCFLaunchDelegate.getMemMapsAttribute(maps, getLaunchConfiguration());
+ for (String id : mem2map.values()) {
+ if (maps.get(id) == null) deleted_maps.add(id);
+ }
+ cmds.add(mem.getChildren(null, done_get_children));
+ }
+ catch (Throwable x) {
+ channel.terminate(x);
+ }
+ }
+ };
+ }
+
+ private void readPathMapConfiguration(ILaunchConfiguration cfg) throws CoreException {
+ String s = cfg.getAttribute(TCFLaunchDelegate.ATTR_PATH_MAP, "");
+ filepath_map = TCFLaunchDelegate.parsePathMapAttribute(s);
+ s = cfg.getAttribute(ILaunchConfiguration.ATTR_SOURCE_LOCATOR_MEMENTO, "");
+ filepath_map.addAll(TCFLaunchDelegate.parseSourceLocatorMemento(s));
+ }
+
+ private void downloadPathMaps(ILaunchConfiguration cfg, final Runnable done) throws Exception {
+ readPathMapConfiguration(cfg);
+ final IPathMap path_map_service = getService(IPathMap.class);
+ path_map_service.set(filepath_map.toArray(new IPathMap.PathMapRule[filepath_map.size()]), new IPathMap.DoneSet() {
+ public void doneSet(IToken token, Exception error) {
+ if (error != null) channel.terminate(error);
+ else done.run();
+ }
+ });
+ }
+
+ private String[] toArgsArray(String file, String cmd) {
+ // Create arguments list from a command line.
+ int i = 0;
+ int l = cmd.length();
+ List<String> arr = new ArrayList<String>();
+ arr.add(file);
+ for (;;) {
+ while (i < l && cmd.charAt(i) == ' ') i++;
+ if (i >= l) break;
+ String s = null;
+ if (cmd.charAt(i) == '"') {
+ i++;
+ StringBuffer bf = new StringBuffer();
+ while (i < l) {
+ char ch = cmd.charAt(i++);
+ if (ch == '"') break;
+ if (ch == '\\' && i < l) ch = cmd.charAt(i++);
+ bf.append(ch);
+ }
+ s = bf.toString();
+ }
+ else {
+ int i0 = i;
+ while (i < l && cmd.charAt(i) != ' ') i++;
+ s = cmd.substring(i0, i);
+ }
+ arr.add(s);
+ }
+ return arr.toArray(new String[arr.size()]);
+ }
+
+ private void copyFileToRemoteTarget(String local_file, String remote_file, final Runnable done) {
+ if (local_file == null) {
+ channel.terminate(new Exception("Program does not exist"));
+ return;
+ }
+ final IFileSystem fs = channel.getRemoteService(IFileSystem.class);
+ if (fs == null) {
+ channel.terminate(new Exception(
+ "Cannot download program file: target does not provide File System service"));
+ return;
+ }
+ try {
+ final InputStream inp = new FileInputStream(local_file);
+ int flags = IFileSystem.TCF_O_WRITE | IFileSystem.TCF_O_CREAT | IFileSystem.TCF_O_TRUNC;
+ fs.open(remote_file, flags, null, new IFileSystem.DoneOpen() {
+
+ IFileHandle handle;
+ long offset = 0;
+ final Set<IToken> cmds = new HashSet<IToken>();
+ final byte[] buf = new byte[0x1000];
+
+ public void doneOpen(IToken token, FileSystemException error, IFileHandle handle) {
+ this.handle = handle;
+ if (error != null) {
+ TCFLaunch.this.error = new Exception("Cannot download program file", error);
+ fireChanged();
+ done.run();
+ }
+ else {
+ write_next();
+ }
+ }
+
+ private void write_next() {
+ try {
+ while (cmds.size() < 8) {
+ int rd = inp.read(buf);
+ if (rd < 0) {
+ close();
+ break;
+ }
+ cmds.add(fs.write(handle, offset, buf, 0, rd, new IFileSystem.DoneWrite() {
+
+ public void doneWrite(IToken token, FileSystemException error) {
+ cmds.remove(token);
+ if (error != null) channel.terminate(error);
+ else write_next();
+ }
+ }));
+ offset += rd;
+ }
+ }
+ catch (Throwable x) {
+ channel.terminate(x);
+ }
+ }
+
+ private void close() {
+ if (cmds.size() > 0) return;
+ try {
+ inp.close();
+ fs.close(handle, new IFileSystem.DoneClose() {
+
+ public void doneClose(IToken token, FileSystemException error) {
+ if (error != null) channel.terminate(error);
+ else done.run();
+ }
+ });
+ }
+ catch (Throwable x) {
+ channel.terminate(x);
+ }
+ }
+ });
+ }
+ catch (Throwable x) {
+ channel.terminate(x);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private void startRemoteProcess(final ILaunchConfiguration cfg) throws Exception {
+ final String project = cfg.getAttribute(TCFLaunchDelegate.ATTR_PROJECT_NAME, "");
+ final String local_file = cfg.getAttribute(TCFLaunchDelegate.ATTR_LOCAL_PROGRAM_FILE, "");
+ final String remote_file = cfg.getAttribute(TCFLaunchDelegate.ATTR_REMOTE_PROGRAM_FILE, "");
+ if (local_file.length() != 0 && remote_file.length() != 0) {
+ // Download executable file
+ new LaunchStep() {
+ @Override
+ void start() throws Exception {
+ copyFileToRemoteTarget(TCFLaunchDelegate.getProgramPath(project, local_file), remote_file, this);
+ }
+ };
+ }
+ final String attach_to_process = getAttribute("attach_to_process");
+ if (attach_to_process != null) {
+ final IProcesses ps = channel.getRemoteService(IProcesses.class);
+ if (ps == null) throw new Exception("Target does not provide Processes service");
+ // Attach the process
+ new LaunchStep() {
+ @Override
+ void start() {
+ IProcesses.DoneGetContext done = new IProcesses.DoneGetContext() {
+ public void doneGetContext(IToken token, final Exception error, final ProcessContext process) {
+ if (error != null) {
+ channel.terminate(error);
+ }
+ else {
+ process.attach(new IProcesses.DoneCommand() {
+ public void doneCommand(IToken token, final Exception error) {
+ if (error != null) {
+ channel.terminate(error);
+ }
+ else {
+ context_filter = new HashSet<String>();
+ context_filter.add(process.getID());
+ TCFLaunch.this.process = process;
+ ps.addListener(prs_listener);
+ connectProcessStreams();
+ done();
+ }
+ }
+ });
+ }
+ }
+ };
+ ps.getContext(attach_to_process, done);
+ }
+ };
+ }
+ else if (local_file.length() != 0 || remote_file.length() != 0) {
+ final IProcesses ps = channel.getRemoteService(IProcesses.class);
+ if (ps == null) throw new Exception("Target does not provide Processes service");
+ final boolean append = cfg.getAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, true);
+ if (append) {
+ // Get system environment variables
+ new LaunchStep() {
+ @Override
+ void start() throws Exception {
+ ps.getEnvironment(new IProcesses.DoneGetEnvironment() {
+ public void doneGetEnvironment(IToken token, Exception error, Map<String,String> env) {
+ if (error != null) {
+ channel.terminate(error);
+ }
+ else {
+ if (env != null) process_env.putAll(env);
+ done();
+ }
+ }
+ });
+ }
+ };
+ }
+ final String dir = cfg.getAttribute(TCFLaunchDelegate.ATTR_WORKING_DIRECTORY, "");
+ final String args = cfg.getAttribute(TCFLaunchDelegate.ATTR_PROGRAM_ARGUMENTS, "");
+ final Map<String,String> env = cfg.getAttribute(ILaunchManager.ATTR_ENVIRONMENT_VARIABLES, (Map<String,String>)null);
+ final boolean attach_children = cfg.getAttribute(TCFLaunchDelegate.ATTR_ATTACH_CHILDREN, true);
+ final boolean use_terminal = cfg.getAttribute(TCFLaunchDelegate.ATTR_USE_TERMINAL, true);
+ // Start the process
+ new LaunchStep() {
+ @Override
+ void start() {
+ if (env != null) process_env.putAll(env);
+ String file = remote_file;
+ if (file == null || file.length() == 0) file = TCFLaunchDelegate.getProgramPath(project, local_file);
+ if (file == null || file.length() == 0) {
+ channel.terminate(new Exception("Program file does not exist"));
+ return;
+ }
+ IProcesses.DoneStart done = new IProcesses.DoneStart() {
+ public void doneStart(IToken token, final Exception error, ProcessContext process) {
+ process_start_command = null;
+ if (error != null) {
+ for (String id : new HashSet<String>(stream_ids.keySet())) disconnectStream(id);
+ Protocol.sync(new Runnable() {
+ public void run() {
+ channel.terminate(error);
+ }
+ });
+ }
+ else {
+ context_filter = new HashSet<String>();
+ context_filter.add(process.getID());
+ TCFLaunch.this.process = process;
+ ps.addListener(prs_listener);
+ connectProcessStreams();
+ done();
+ }
+ }
+ };
+ String[] args_arr = toArgsArray(file, args);
+ IProcessesV1 ps_v1 = channel.getRemoteService(IProcessesV1.class);
+ if (ps_v1 != null) {
+ Map<String,Object> params = new HashMap<String,Object>();
+ if (mode.equals(ILaunchManager.DEBUG_MODE)) {
+ params.put(IProcessesV1.START_ATTACH, true);
+ if (attach_children) params.put(IProcessesV1.START_ATTACH_CHILDREN, true);
+ }
+ if (use_terminal) params.put(IProcessesV1.START_USE_TERMINAL, true);
+ process_start_command = ps_v1.start(dir, file, args_arr, process_env, params, done);
+ }
+ else {
+ boolean attach = mode.equals(ILaunchManager.DEBUG_MODE);
+ process_start_command = ps.start(dir, file, args_arr, process_env, attach, done);
+ }
+ }
+ };
+ if (mode.equals(ILaunchManager.DEBUG_MODE)) {
+ // Get process signal list
+ new LaunchStep() {
+ @Override
+ void start() {
+ ps.getSignalList(process.getID(), new IProcesses.DoneGetSignalList() {
+ public void doneGetSignalList(IToken token, Exception error, Collection<Map<String,Object>> list) {
+ if (error != null) Activator.log("Can't get process signal list", error);
+ process_signals = list;
+ done();
+ }
+ });
+ }
+ };
+ // Set process signal masks
+ String dont_stop = cfg.getAttribute(TCFLaunchDelegate.ATTR_SIGNALS_DONT_STOP, "");
+ String dont_pass = cfg.getAttribute(TCFLaunchDelegate.ATTR_SIGNALS_DONT_PASS, "");
+ final int no_stop = dont_stop.length() > 0 ? Integer.parseInt(dont_stop, 16) : 0;
+ final int no_pass = dont_pass.length() > 0 ? Integer.parseInt(dont_pass, 16) : 0;
+ if (no_stop != 0 || no_pass != 0) {
+ new LaunchStep() {
+ @Override
+ void start() {
+ final HashSet<IToken> cmds = new HashSet<IToken>();
+ final IProcesses.DoneCommand done_set_mask = new IProcesses.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ cmds.remove(token);
+ if (error != null) channel.terminate(error);
+ else if (cmds.size() == 0) done();
+ }
+ };
+ cmds.add(ps.setSignalMask(process.getID(), no_stop, no_pass, done_set_mask));
+ final IRunControl rc = channel.getRemoteService(IRunControl.class);
+ if (rc != null) {
+ final IRunControl.DoneGetChildren done_get_children = new IRunControl.DoneGetChildren() {
+ public void doneGetChildren(IToken token, Exception error, String[] context_ids) {
+ if (context_ids != null) {
+ for (String id : context_ids) {
+ cmds.add(ps.setSignalMask(id, no_stop, no_pass, done_set_mask));
+ cmds.add(rc.getChildren(id, this));
+ }
+ }
+ cmds.remove(token);
+ if (error != null) channel.terminate(error);
+ else if (cmds.size() == 0) done();
+ }
+ };
+ cmds.add(rc.getChildren(process.getID(), done_get_children));
+ }
+ }
+ };
+ }
+ }
+ }
+ }
+
+ private void connectProcessStreams() {
+ assert process_start_command == null;
+ final IStreams streams = getService(IStreams.class);
+ if (streams == null) return;
+ final String inp_id = (String)process.getProperties().get(IProcesses.PROP_STDIN_ID);
+ final String out_id = (String)process.getProperties().get(IProcesses.PROP_STDOUT_ID);
+ final String err_id = (String)process.getProperties().get(IProcesses.PROP_STDERR_ID);
+ for (final String id : stream_ids.keySet().toArray(new String[stream_ids.size()])) {
+ if (id.equals(inp_id)) {
+ process_input_stream_id = id;
+ }
+ else if (id.equals(out_id)) {
+ connectStream(id, 0);
+ }
+ else if (id.equals(err_id)) {
+ connectStream(id, 1);
+ }
+ else {
+ disconnectStream(id);
+ }
+ }
+ }
+
+ private void connectStream(final String id, final int no) {
+ final String peocess_id = process.getID();
+ final IStreams streams = getService(IStreams.class);
+ IStreams.DoneRead done = new IStreams.DoneRead() {
+ public void doneRead(IToken token, Exception error, int lost_size, byte[] data, boolean eos) {
+ if (stream_ids.get(id) == null) return;
+ if (lost_size > 0) {
+ Exception x = new IOException("Process output data lost due buffer overflow");
+ for (LaunchListener l : getListeners()) l.onProcessStreamError(TCFLaunch.this, peocess_id, no, x, lost_size);
+ }
+ if (data != null && data.length > 0) {
+ for (LaunchListener l : getListeners()) l.onProcessOutput(TCFLaunch.this, peocess_id, no, data);
+ }
+ if (error != null) {
+ for (LaunchListener l : getListeners()) l.onProcessStreamError(TCFLaunch.this, peocess_id, no, error, 0);
+ }
+ if (eos || error != null) {
+ disconnectStream(id);
+ }
+ else {
+ streams.read(id, 0x1000, this);
+ }
+ }
+ };
+ streams.read(id, 0x1000, done);
+ streams.read(id, 0x1000, done);
+ streams.read(id, 0x1000, done);
+ streams.read(id, 0x1000, done);
+ }
+
+ private void disconnectStream(String id) {
+ assert stream_ids.get(id) != null;
+ stream_ids.remove(id);
+ if (channel.getState() != IChannel.STATE_OPEN) return;
+ IStreams streams = getService(IStreams.class);
+ streams.disconnect(id, new IStreams.DoneDisconnect() {
+ public void doneDisconnect(IToken token, Exception error) {
+ if (channel.getState() != IChannel.STATE_OPEN) return;
+ if (error != null) channel.terminate(error);
+ }
+ });
+ }
+
+ protected void runShutdownSequence(final Runnable done) {
+ done.run();
+ }
+
+ /*--------------------------------------------------------------------------------------------*/
+
+ public Throwable getError() {
+ return error;
+ }
+
+ public void setError(Throwable x) {
+ error = x;
+ if (x != null) {
+ if (channel != null && channel.getState() == IChannel.STATE_OPEN) {
+ channel.terminate(x);
+ }
+ else if (!connecting) {
+ disconnected = true;
+ }
+ }
+ fireChanged();
+ }
+
+ public TCFBreakpointsStatus getBreakpointsStatus() {
+ return breakpoints_status;
+ }
+
+ /**
+ * Check if the agent supports setting of user defined memory map entries
+ * for a context that does not exits yet.
+ * @return true if memory map preloading is supported.
+ */
+ public boolean isMemoryMapPreloadingSupported() {
+ return supports_memory_map_preloading;
+ }
+
+ public static void addListener(LaunchListener listener) {
+ assert Protocol.isDispatchThread();
+ listeners.add(listener);
+ listeners_array = null;
+ }
+
+ public static void removeListener(LaunchListener listener) {
+ assert Protocol.isDispatchThread();
+ listeners.remove(listener);
+ listeners_array = null;
+ }
+
+ public void launchConfigurationChanged(final ILaunchConfiguration cfg) {
+ super.launchConfigurationChanged(cfg);
+ if (!cfg.equals(getLaunchConfiguration())) return;
+ if (channel != null && channel.getState() == IChannel.STATE_OPEN) {
+ new TCFTask<Boolean>(channel) {
+ public void run() {
+ try {
+ if (update_memory_maps != null) update_memory_maps.run();
+ if (filepath_map != null) {
+ readPathMapConfiguration(cfg);
+ final IPathMap path_map_service = getService(IPathMap.class);
+ path_map_service.set(filepath_map.toArray(new IPathMap.PathMapRule[filepath_map.size()]), new IPathMap.DoneSet() {
+ public void doneSet(IToken token, Exception error) {
+ if (error != null) channel.terminate(error);
+ done(false);
+ }
+ });
+ }
+ else {
+ done(true);
+ }
+ }
+ catch (Throwable x) {
+ channel.terminate(x);
+ done(false);
+ }
+ }
+ }.getE();
+ // TODO: update signal masks when launch configuration changes
+ }
+ }
+
+ /** Thread safe method */
+ public IChannel getChannel() {
+ return channel;
+ }
+
+ public IProcesses.ProcessContext getProcessContext() {
+ return process;
+ }
+
+ public void writeProcessInputStream(byte[] buf, int pos, final int len) throws Exception {
+ assert Protocol.isDispatchThread();
+ final String id = process_input_stream_id;
+ if (channel.getState() != IChannel.STATE_OPEN) throw new IOException("Connection closed");
+ if (process == null) throw new IOException("No target process");
+ final String prs = process.getID();
+ IStreams streams = getService(IStreams.class);
+ if (streams == null) throw new IOException("Streams service not available");
+ if (stream_ids.get(id) == null) throw new IOException("Input stream not available");
+ streams.write(id, buf, pos, len, new IStreams.DoneWrite() {
+ public void doneWrite(IToken token, Exception error) {
+ if (error == null) return;
+ if (stream_ids.get(id) == null) return;
+ for (LaunchListener l : getListeners()) l.onProcessStreamError(TCFLaunch.this, prs, 0, error, len);
+ disconnectStream(id);
+ }
+ });
+ }
+
+ public boolean isConnecting() {
+ return connecting;
+ }
+
+ public void onLastContextRemoved() {
+ ILaunchConfiguration cfg = getLaunchConfiguration();
+ try {
+ if (cfg.getAttribute(TCFLaunchDelegate.ATTR_DISCONNECT_ON_CTX_EXIT, true)) {
+ last_context_exited = true;
+ closeChannel();
+ }
+ }
+ catch (Throwable e) {
+ Activator.log("Cannot access launch configuration", e);
+ }
+ }
+
+ public void closeChannel() {
+ assert Protocol.isDispatchThread();
+ if (channel == null) return;
+ if (channel.getState() == IChannel.STATE_CLOSED) return;
+ if (disconnecting) return;
+ disconnecting = true;
+ final Set<IToken> cmds = new HashSet<IToken>();
+ if (process != null && !process_exited) {
+ cmds.add(process.terminate(new IProcesses.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ cmds.remove(token);
+ if (error != null) channel.terminate(error);
+ else if (cmds.isEmpty()) channel.close();
+ }
+ }));
+ }
+ IStreams streams = getService(IStreams.class);
+ for (String id : stream_ids.keySet()) {
+ cmds.add(streams.disconnect(id, new IStreams.DoneDisconnect() {
+ public void doneDisconnect(IToken token, Exception error) {
+ cmds.remove(token);
+ if (error != null) channel.terminate(error);
+ else if (cmds.isEmpty()) channel.close();
+ }
+ }));
+ }
+ stream_ids.clear();
+ process_input_stream_id = null;
+ if (cmds.isEmpty()) channel.close();
+ }
+
+ public IPeer getPeer() {
+ assert Protocol.isDispatchThread();
+ return channel.getRemotePeer();
+ }
+
+ public String getPeerName() {
+ // Safe to call from any thread.
+ return peer_name;
+ }
+
+ public <V extends IService> V getService(Class<V> cls) {
+ assert Protocol.isDispatchThread();
+ return channel.getRemoteService(cls);
+ }
+
+ public boolean canDisconnect() {
+ return !disconnected;
+ }
+
+ public boolean isDisconnected() {
+ return disconnected;
+ }
+
+ public void disconnect() throws DebugException {
+ try {
+ new TCFTask<Boolean>() {
+ public void run() {
+ closeChannel();
+ done(true);
+ }
+ }.get();
+ }
+ catch (IllegalStateException x) {
+ // Don't report this exception - it means Eclipse is being shut down
+ disconnected = true;
+ }
+ catch (Exception x) {
+ throw new TCFError(x);
+ }
+ }
+
+ public boolean canTerminate() {
+ return false;
+ }
+
+ public boolean isTerminated() {
+ return disconnected;
+ }
+
+ public void terminate() throws DebugException {
+ }
+
+ public boolean isExited() {
+ return last_context_exited;
+ }
+
+ public int getExitCode() {
+ return process_exit_code;
+ }
+
+ public Collection<Map<String,Object>> getSignalList() {
+ return process_signals;
+ }
+
+ public ArrayList<PathMapRule> getFilePathMap() {
+ assert Protocol.isDispatchThread();
+ return filepath_map;
+ }
+
+ /**
+ * Activate TCF launch: open communication channel and perform all necessary launch steps.
+ * @param mode - on of launch mode constants defined in ILaunchManager.
+ * @param id - TCF peer ID.
+ */
+ public void launchTCF(String mode, String id) {
+ assert Protocol.isDispatchThread();
+ this.mode = mode;
+ try {
+ if (id == null || id.length() == 0) throw new IOException("Invalid peer ID");
+ redirection_path.clear();
+ for (;;) {
+ int i = id.indexOf('/');
+ if (i <= 0) {
+ redirection_path.add(id);
+ break;
+ }
+ redirection_path.add(id.substring(0, i));
+ id = id.substring(i + 1);
+ }
+ String id0 = redirection_path.removeFirst();
+ IPeer peer = Protocol.getLocator().getPeers().get(id0);
+ if (peer == null) throw new Exception("Cannot locate peer " + id0);
+ peer_name = peer.getName();
+ channel = peer.openChannel();
+ channel.addChannelListener(new IChannel.IChannelListener() {
+
+ public void onChannelOpened() {
+ try {
+ peer_name = getPeer().getName();
+ onConnected();
+ }
+ catch (Throwable x) {
+ channel.terminate(x);
+ }
+ }
+
+ public void congestionLevel(int level) {
+ }
+
+ public void onChannelClosed(Throwable error) {
+ channel.removeChannelListener(this);
+ onDisconnected(error);
+ }
+
+ });
+ assert channel.getState() == IChannel.STATE_OPENING;
+ connecting = true;
+ }
+ catch (Throwable e) {
+ onDisconnected(e);
+ }
+ }
+
+ /****************************************************************************************************************/
+
+ private long getActionTimeStamp(String id) {
+ Long l = context_action_timestamps.get(id);
+ if (l == null) return 0;
+ return l.longValue();
+ }
+
+ private void startAction(final String id) {
+ if (active_actions.get(id) != null) return;
+ LinkedList<TCFAction> list = context_action_queue.get(id);
+ if (list == null || list.size() == 0) return;
+ final TCFAction action = list.removeFirst();
+ if (list.size() == 0) context_action_queue.remove(id);
+ active_actions.put(id, action);
+ final long timestamp = getActionTimeStamp(id);
+ long time = System.currentTimeMillis();
+ Protocol.invokeLater(timestamp + actions_interval - time, new Runnable() {
+ public void run() {
+ if (active_actions.get(id) != action) return;
+ long time = System.currentTimeMillis();
+ synchronized (pending_clients) {
+ if (pending_clients.size() > 0) {
+ if (time - timestamp < actions_interval + 1000) {
+ Protocol.invokeLater(20, this);
+ return;
+ }
+ pending_clients.clear();
+ }
+ else if (time < pending_clients_timestamp + 10) {
+ Protocol.invokeLater(pending_clients_timestamp + 10 - time, this);
+ return;
+ }
+ }
+ context_action_timestamps.put(id, time);
+ for (ActionsListener l : action_listeners) l.onContextActionStart(action);
+ action.run();
+ }
+ });
+ }
+
+ /**
+ * Add an object to the set of pending clients.
+ * Actions execution will be delayed until the set is empty,
+ * but not longer then 1 second.
+ * @param client
+ */
+ public void addPendingClient(Object client) {
+ synchronized (pending_clients) {
+ pending_clients.add(client);
+ pending_clients_timestamp = System.currentTimeMillis();
+ }
+ }
+
+ /**
+ * Remove an object from the set of pending clients.
+ * Actions execution resumes when the set becomes empty.
+ * @param client
+ */
+ public void removePendingClient(Object client) {
+ synchronized (pending_clients) {
+ if (pending_clients.remove(client) && pending_clients.size() == 0) {
+ pending_clients_timestamp = System.currentTimeMillis();
+ }
+ }
+ }
+
+ /**
+ * Set minimum interval between context actions execution.
+ * @param interval - minimum interval in milliseconds.
+ */
+ public void setContextActionsInterval(long interval) {
+ actions_interval = interval;
+ }
+
+ /**
+ * Add a context action to actions queue.
+ * Examples of context actions are resume/suspend/step commands,
+ * which were requested by a user.
+ * @param action
+ */
+ public void addContextAction(TCFAction action) {
+ assert Protocol.isDispatchThread();
+ String id = action.getContextID();
+ LinkedList<TCFAction> list = context_action_queue.get(id);
+ if (list == null) context_action_queue.put(id, list = new LinkedList<TCFAction>());
+ int priority = action.getPriority();
+ for (ListIterator<TCFAction> i = list.listIterator();;) {
+ if (i.hasNext()) {
+ if (priority <= i.next().getPriority()) continue;
+ i.previous();
+ }
+ i.add(action);
+ break;
+ }
+ startAction(id);
+ }
+
+ /**
+ * Set action result for given context ID.
+ * Action results are usually presented to a user same way as context suspend reasons.
+ * @param id - debug context ID.
+ * @param result - a string to be shown to user.
+ */
+ public void setContextActionResult(String id, String result) {
+ assert Protocol.isDispatchThread();
+ for (ActionsListener l : action_listeners) l.onContextActionResult(id, result);
+ }
+
+ /**
+ * Remove an action from the queue.
+ * The method should be called when the action execution is done.
+ * @param action
+ */
+ public void removeContextAction(TCFAction action) {
+ assert Protocol.isDispatchThread();
+ String id = action.getContextID();
+ assert active_actions.get(id) == action;
+ active_actions.remove(id);
+ for (ActionsListener l : action_listeners) l.onContextActionDone(action);
+ startAction(id);
+ }
+
+ /**
+ * Remove all actions from the queue of a debug context.
+ * @param id - debug context ID.
+ */
+ public void removeContextActions(String id) {
+ assert Protocol.isDispatchThread();
+ context_action_queue.remove(id);
+ context_action_timestamps.remove(id);
+ }
+
+ /**
+ * Get action queue size of a debug context.
+ * @param id - debug context ID.
+ * @return count of pending actions.
+ */
+ public int getContextActionsCount(String id) {
+ assert Protocol.isDispatchThread();
+ LinkedList<TCFAction> list = context_action_queue.get(id);
+ int n = list == null ? 0 : list.size();
+ if (active_actions.get(id) != null) n++;
+ return n;
+ }
+
+ /**
+ * Add a listener that will be notified when an action execution is started or finished,
+ * or when an action result is posted.
+ * @param l - action listener.
+ */
+ public void addActionsListener(ActionsListener l) {
+ action_listeners.add(l);
+ }
+
+ /**
+ * Remove an action listener that was registered with addActionsListener().
+ * @param l - action listener.
+ */
+ public void removeActionsListener(ActionsListener l) {
+ action_listeners.remove(l);
+ }
+
+ public Set<String> getContextFilter() {
+ return context_filter;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFMemoryRegion.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFMemoryRegion.java
new file mode 100644
index 000000000..8017fe254
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFMemoryRegion.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2010 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.math.BigInteger;
+import java.util.Map;
+
+import org.eclipse.tm.tcf.protocol.JSON;
+import org.eclipse.tm.tcf.services.IMemoryMap;
+import org.eclipse.tm.tcf.services.IMemoryMap.MemoryRegion;
+
+public class TCFMemoryRegion implements MemoryRegion, Comparable<TCFMemoryRegion> {
+
+ private final Map<String,Object> props;
+
+ public final BigInteger addr;
+ public final BigInteger size;
+
+ public TCFMemoryRegion(Map<String,Object> props) {
+ this.props = props;
+ this.addr = JSON.toBigInteger((Number)props.get(IMemoryMap.PROP_ADDRESS));
+ this.size = JSON.toBigInteger((Number)props.get(IMemoryMap.PROP_SIZE));
+ }
+
+ public Number getAddress() {
+ return addr;
+ }
+
+ public Number getSize() {
+ return size;
+ }
+
+ public Number getOffset() {
+ return (Number)props.get(IMemoryMap.PROP_OFFSET);
+ }
+
+ public String getFileName() {
+ return (String)props.get(IMemoryMap.PROP_FILE_NAME);
+ }
+
+ public String getSectionName() {
+ return (String)props.get(IMemoryMap.PROP_SECTION_NAME);
+ }
+
+ public int getFlags() {
+ Number n = (Number)props.get(IMemoryMap.PROP_FLAGS);
+ if (n != null) return n.intValue();
+ return 0;
+ }
+
+ public Map<String,Object> getProperties() {
+ return props;
+ }
+
+ public int compareTo(TCFMemoryRegion r) {
+ if (addr == null && r.addr == null) return 0;
+ if (addr == null) return -1;
+ if (r.addr == null) return +1;
+ return addr.compareTo(r.addr);
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFSourceRef.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFSourceRef.java
new file mode 100644
index 000000000..79c93a428
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/model/TCFSourceRef.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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.math.BigInteger;
+
+import org.eclipse.tm.tcf.services.ILineNumbers;
+
+/**
+ * Objects of this class represent a mapping between an address and source code area.
+ */
+public class TCFSourceRef {
+ public String context_id;
+ public int address_size;
+ public BigInteger address;
+ public ILineNumbers.CodeArea area;
+ public Throwable error;
+
+ public String toString() {
+ StringBuffer bf = new StringBuffer();
+ bf.append('[');
+ bf.append(context_id);
+ bf.append(',');
+ bf.append(address_size);
+ bf.append(',');
+ bf.append(address);
+ bf.append(',');
+ bf.append(area);
+ bf.append(',');
+ bf.append(error);
+ bf.append(']');
+ return bf.toString();
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/ITCFTest.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/ITCFTest.java
new file mode 100644
index 000000000..1fc67adbd
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/ITCFTest.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tm.internal.tcf.debug.tests;
+
+/**
+ * Each (sub)test in TCF Test Suite should implement this interface.
+ */
+interface ITCFTest {
+
+ /**
+ * Start execution of the test.
+ */
+ void start();
+
+ /**
+ * Check if the test don't need the context to stay suspended.
+ * @param id - run control context ID.
+ * @return true if it is OK to resume the context.
+ */
+ boolean canResume(String id);
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/Main.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/Main.java
new file mode 100644
index 000000000..f5f0e27ec
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/Main.java
@@ -0,0 +1,164 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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.tests;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+
+import org.eclipse.tm.tcf.core.TransientPeer;
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IEventQueue;
+import org.eclipse.tm.tcf.protocol.IPeer;
+import org.eclipse.tm.tcf.protocol.Protocol;
+
+/**
+ * This class is user to run TCF test suite from command line.
+ */
+public class Main {
+
+ private static class EventQueue extends Thread implements IEventQueue {
+
+ private final LinkedList<Runnable> queue = new LinkedList<Runnable>();
+
+ EventQueue() {
+ setName("TCF Event Dispatcher");
+ start();
+ }
+
+ public void run() {
+ try {
+ while (true) {
+ Runnable r = null;
+ synchronized (this) {
+ while (queue.size() == 0) wait();
+ r = queue.removeFirst();
+ }
+ r.run();
+ }
+ }
+ catch (Throwable x) {
+ x.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ public synchronized int getCongestion() {
+ int n = queue.size() - 100;
+ if (n > 100) n = 100;
+ return n;
+ }
+
+ public synchronized void invokeLater(Runnable runnable) {
+ queue.add(runnable);
+ notify();
+ }
+
+ public boolean isDispatchThread() {
+ return Thread.currentThread() == this;
+ }
+ }
+
+ private static class RemotePeer extends TransientPeer {
+
+ private final ArrayList<Map<String,String>> attrs;
+
+ public RemotePeer(ArrayList<Map<String,String>> attrs) {
+ super(attrs.get(0));
+ this.attrs = attrs;
+ }
+
+ public IChannel openChannel() {
+ assert Protocol.isDispatchThread();
+ IChannel c = super.openChannel();
+ for (int i = 1; i < attrs.size(); i++) c.redirect(attrs.get(i));
+ return c;
+ }
+ }
+
+ private static IPeer getPeer(String[] arr) {
+ ArrayList<Map<String,String>> l = new ArrayList<Map<String,String>>();
+ for (String s : arr) {
+ Map<String,String> map = new HashMap<String,String>();
+ int len = s.length();
+ int i = 0;
+ while (i < len) {
+ int i0 = i;
+ while (i < len && s.charAt(i) != '=' && s.charAt(i) != 0) i++;
+ int i1 = i;
+ if (i < len && s.charAt(i) == '=') i++;
+ int i2 = i;
+ while (i < len && s.charAt(i) != ':') i++;
+ int i3 = i;
+ if (i < len && s.charAt(i) == ':') i++;
+ String key = s.substring(i0, i1);
+ String val = s.substring(i2, i3);
+ map.put(key, val);
+ }
+ l.add(map);
+ }
+ return new RemotePeer(l);
+ }
+
+ private static void runTestSuite(IPeer peer) {
+ TCFTestSuite.TestListener listener = new TCFTestSuite.TestListener() {
+
+ public void done(Collection<Throwable> errors) {
+ if (errors == null || errors.isEmpty()) {
+ System.out.println("No errors detected.");
+ System.exit(0);
+ }
+ for (Throwable x : errors) {
+ x.printStackTrace(System.out);
+ }
+ System.exit(3);
+ }
+
+ public void progress(String label, int done, int total) {
+ if (label != null) System.out.println(label);
+ }
+
+ };
+ try {
+ new TCFTestSuite(peer, listener, null, null);
+ }
+ catch (Throwable x) {
+ System.err.println("Cannot start test suite:");
+ x.printStackTrace();
+ System.exit(2);
+ }
+ }
+
+ /**
+ * Command line should contain peer description string, for example:
+ * "ID=Test:TransportName=TCP:Host=127.0.0.1:Port=1534"
+ */
+ public static void main(final String[] args) {
+ if (args.length < 1) {
+ System.err.println("Missing command line argument - peer identification string");
+ System.exit(4);
+ }
+ Protocol.setEventQueue(new EventQueue());
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ runTestSuite(getPeer(args));
+ }
+ });
+ Protocol.invokeLater(10 * 60 * 1000, new Runnable() {
+ public void run() {
+ System.err.println("Error: timeout - test's not finished in 10 min");
+ System.exit(5);
+ }
+ });
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/RunControl.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/RunControl.java
new file mode 100644
index 000000000..e111a5907
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/RunControl.java
@@ -0,0 +1,280 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tm.internal.tcf.debug.tests;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.protocol.Protocol;
+import org.eclipse.tm.tcf.services.IDiagnostics;
+import org.eclipse.tm.tcf.services.IRunControl;
+import org.eclipse.tm.tcf.services.IRunControl.RunControlContext;
+
+class RunControl {
+
+ private final TCFTestSuite test_suite;
+ private final IChannel channel;
+ private int channel_id;
+ private final IRunControl rc_service;
+ private final HashSet<String> suspended_ctx_ids = new HashSet<String>();
+ private final HashSet<IToken> get_state_cmds = new HashSet<IToken>();
+ private final HashMap<String,IToken> resume_cmds = new HashMap<String,IToken>();
+ private final HashSet<String> pending_resume_ids = new HashSet<String>();
+ private final HashMap<String,IRunControl.RunControlContext> ctx_map = new HashMap<String,IRunControl.RunControlContext>();
+ private final Random rnd = new Random();
+
+ private boolean enable_trace;
+
+ private boolean sync_pending;
+
+ private final IRunControl.RunControlListener listener = new IRunControl.RunControlListener() {
+
+ public void contextAdded(RunControlContext[] contexts) {
+ for (IRunControl.RunControlContext ctx : contexts) {
+ ctx_map.put(ctx.getID(), ctx);
+ }
+ }
+
+ public void contextChanged(RunControlContext[] contexts) {
+ for (IRunControl.RunControlContext ctx : contexts) {
+ ctx_map.put(ctx.getID(), ctx);
+ }
+ }
+
+ public void contextRemoved(String[] context_ids) {
+ for (String id : context_ids) {
+ ctx_map.remove(id);
+ test_suite.getCanceledTests().remove(id);
+ suspended_ctx_ids.remove(id);
+ }
+ }
+
+ public void contextSuspended(final String id, String pc, String reason, Map<String,Object> params) {
+ if (enable_trace) System.out.println("" + channel_id + " suspended " + id);
+ suspended_ctx_ids.add(id);
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ resume(id, IRunControl.RM_RESUME);
+ }
+ });
+ }
+
+ public void contextResumed(String id) {
+ if (enable_trace) System.out.println("" + channel_id + " resumed " + id);
+ suspended_ctx_ids.remove(id);
+ pending_resume_ids.remove(id);
+ }
+
+ public void containerSuspended(String context, String pc, String reason, Map<String, Object> params, String[] suspended_ids) {
+ if (enable_trace) {
+ StringBuffer bf = new StringBuffer();
+ for (String id : suspended_ids) {
+ if (bf.length() > 0) bf.append(',');
+ bf.append(id);
+ }
+ System.out.println("" + channel_id + " suspended " + bf);
+ }
+ for (String id : suspended_ids) {
+ suspended_ctx_ids.add(id);
+ resume(id, IRunControl.RM_RESUME);
+ }
+ }
+
+ public void containerResumed(String[] context_ids) {
+ if (enable_trace) {
+ StringBuffer bf = new StringBuffer();
+ for (String id : context_ids) {
+ if (bf.length() > 0) bf.append(',');
+ bf.append(id);
+ }
+ System.out.println("" + channel_id + " resumed " + bf);
+ }
+ for (String id : context_ids) {
+ suspended_ctx_ids.remove(id);
+ pending_resume_ids.remove(id);
+ }
+ }
+
+ public void contextException(String context, String msg) {
+ }
+ };
+
+ RunControl(TCFTestSuite test_suite, IChannel channel, int channel_id) {
+ this.test_suite = test_suite;
+ this.channel = channel;
+ this.channel_id = channel_id;
+ rc_service = channel.getRemoteService(IRunControl.class);
+ if (rc_service != null) {
+ rc_service.addListener(listener);
+ getState();
+ startTimer();
+ }
+ enable_trace = System.getProperty("org.eclipse.tm.tcf.debug.tracing.tests.runcontrol") != null;
+ }
+
+ private void getState() {
+ get_state_cmds.add(rc_service.getChildren(null, new IRunControl.DoneGetChildren() {
+ public void doneGetChildren(IToken token, Exception error, String[] context_ids) {
+ get_state_cmds.remove(token);
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ for (final String id : context_ids) {
+ get_state_cmds.add(rc_service.getChildren(id, this));
+ get_state_cmds.add(rc_service.getContext(id, new IRunControl.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, RunControlContext context) {
+ get_state_cmds.remove(token);
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ ctx_map.put(id, context);
+ if (context.hasState()) {
+ get_state_cmds.add(context.getState(new IRunControl.DoneGetState() {
+ public void doneGetState(IToken token, Exception error, boolean suspended,
+ String pc, String reason, Map<String, Object> params) {
+ get_state_cmds.remove(token);
+ if (error != null) {
+ if (ctx_map.get(id) != null) exit(new Exception(
+ "Cannot get context state", error));
+ }
+ else {
+ if (suspended) suspended_ctx_ids.add(id);
+ getStateDone();
+ }
+ }
+ }));
+ }
+ getStateDone();
+ }
+ }
+ }));
+ }
+ getStateDone();
+ }
+ }
+ }));
+ }
+
+ private void getStateDone() {
+ if (get_state_cmds.size() > 0) return;
+ if (channel.getState() != IChannel.STATE_OPEN) return;
+ if (suspended_ctx_ids.size() > 0) {
+ String[] arr = suspended_ctx_ids.toArray(new String[suspended_ctx_ids.size()]);
+ resume(arr[rnd.nextInt(arr.length)], IRunControl.RM_RESUME);
+ }
+ }
+
+ private void startTimer() {
+ Protocol.invokeLater(50, new Runnable() {
+ public void run() {
+ if (channel.getState() != IChannel.STATE_OPEN) return;
+ if (test_suite.cancel) return;
+ Protocol.invokeLater(50, this);
+ Set<String> s = test_suite.getCanceledTests().keySet();
+ if (s.size() > 0 || suspended_ctx_ids.size() > 0) {
+ Set<String> ids = new HashSet<String>(s);
+ ids.addAll(suspended_ctx_ids);
+ String[] arr = ids.toArray(new String[ids.size()]);
+ resume(arr[rnd.nextInt(arr.length)], IRunControl.RM_RESUME);
+ }
+ }
+ });
+ }
+
+ private void exit(Throwable error) {
+ Collection<ITCFTest> c = test_suite.getActiveTests();
+ ITCFTest[] arr = c.toArray(new ITCFTest[c.size()]);
+ for (ITCFTest t : arr) test_suite.done(t, error);
+ }
+
+ IRunControl.RunControlContext getContext(String id) {
+ return ctx_map.get(id);
+ }
+
+ boolean canResume(String id) {
+ if (sync_pending) return false;
+ if (get_state_cmds.size() > 0) return false;
+ if (resume_cmds.get(id) != null) return false;
+ if (test_suite.getCanceledTests().get(id) == null && !suspended_ctx_ids.contains(id)) return false;
+ IRunControl.RunControlContext ctx = ctx_map.get(id);
+ if (ctx == null) return false;
+ String grp = ctx.getRCGroup();
+ if (grp != null) {
+ for (String s : resume_cmds.keySet()) {
+ IRunControl.RunControlContext c = ctx_map.get(s);
+ if (c == null) return false;
+ if (grp.equals(c.getRCGroup())) return false;
+ }
+ }
+ return true;
+ }
+
+ void resume(final String id, final int mode) {
+ if (!test_suite.canResume(id)) return;
+ assert !sync_pending;
+ sync_pending = true;
+ Protocol.sync(new Runnable() {
+ public void run() {
+ sync_pending = false;
+ if (test_suite.canResume(id)) {
+ assert resume_cmds.get(id) == null;
+ final String test_id = test_suite.getCanceledTests().get(id);
+ if (test_id != null) {
+ if (enable_trace) System.out.println("" + channel_id + " cancel " + id);
+ IDiagnostics diag = channel.getRemoteService(IDiagnostics.class);
+ resume_cmds.put(id, diag.cancelTest(test_id, new IDiagnostics.DoneCancelTest() {
+ public void doneCancelTest(IToken token, Throwable error) {
+ assert resume_cmds.get(id) == token;
+ resume_cmds.remove(id);
+ if (enable_trace) System.out.println("" + channel_id + " done cancel " + error);
+ if (error != null && ctx_map.get(test_id) != null) exit(error);
+ }
+ }));
+ }
+ else {
+ IRunControl.RunControlContext ctx = ctx_map.get(id);
+ if (ctx != null) {
+ pending_resume_ids.add(id);
+ if (enable_trace) System.out.println("" + channel_id + " resume " + mode + " " + id);
+ resume_cmds.put(id, ctx.resume(mode, 1, new IRunControl.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ assert resume_cmds.get(id) == token;
+ resume_cmds.remove(id);
+ if (enable_trace) System.out.println("" + channel_id + " done resume " + error);
+ if (error != null) {
+ pending_resume_ids.remove(id);
+ if (suspended_ctx_ids.contains(id)) exit(error);
+ }
+ else if (pending_resume_ids.contains(id)) {
+ exit(new Exception("Missing contextResumed event"));
+ }
+ }
+ }));
+ }
+ }
+ }
+ }
+ });
+ }
+
+ void cancel(String thread_id, String test_id) {
+ test_suite.getCanceledTests().put(thread_id, test_id);
+ resume(thread_id, 0);
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TCFTestSuite.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TCFTestSuite.java
new file mode 100644
index 000000000..9ffec13c4
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TCFTestSuite.java
@@ -0,0 +1,312 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tm.internal.tcf.debug.tests;
+
+import java.io.IOException;
+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;
+import java.util.Random;
+
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IPeer;
+import org.eclipse.tm.tcf.protocol.Protocol;
+import org.eclipse.tm.tcf.services.IMemoryMap;
+import org.eclipse.tm.tcf.services.IPathMap;
+
+/**
+ * TCF Test Suite implements stress testing of communication channels and capabilities of remote peer.
+ * It is intended to be used before starting a debug session for a first time to make sure the selected
+ * target is stable and reliable.
+ */
+public class TCFTestSuite {
+
+ private final static int NUM_CHANNELS = 4;
+
+ private final TestListener listener;
+ private final IChannel[] channels;
+ private final Map<IChannel,RunControl> run_controls = new HashMap<IChannel, RunControl>();
+ private final LinkedList<Runnable> pending_tests = new LinkedList<Runnable>();
+ private final Collection<Throwable> errors = new ArrayList<Throwable>();
+ private final Map<ITCFTest,IChannel> active_tests = new HashMap<ITCFTest,IChannel>();
+ private final static HashMap<String,String> cancel_test_ids = new HashMap<String,String>();
+
+ private int count_total;
+ private int count_done;
+
+ boolean cancel;
+ boolean canceled;
+ boolean target_lock;
+
+ public interface TestListener {
+ public void progress(String label, int done, int total);
+ public void done(Collection<Throwable> errors);
+ }
+
+ public TCFTestSuite(final IPeer peer, final TestListener listener, final List<IPathMap.PathMapRule> path_map,
+ final Map<String,ArrayList<IMemoryMap.MemoryRegion>> mem_map) throws IOException {
+ this.listener = listener;
+ pending_tests.add(new Runnable() {
+ public void run() {
+ listener.progress("Running Echo Test...", ++count_done, count_total);
+ for (IChannel channel : channels) {
+ active_tests.put(new TestEcho(TCFTestSuite.this, channel), channel);
+ }
+ }
+ });
+ pending_tests.add(new Runnable() {
+ public void run() {
+ listener.progress("Running Echo FP Test...", ++count_done, count_total);
+ for (IChannel channel : channels) {
+ active_tests.put(new TestEchoFP(TCFTestSuite.this, channel), channel);
+ }
+ }
+ });
+ pending_tests.add(new Runnable() {
+ public void run() {
+ listener.progress("Running Echo ERR Test...", ++count_done, count_total);
+ for (IChannel channel : channels) {
+ active_tests.put(new TestEchoERR(TCFTestSuite.this, channel), channel);
+ }
+ }
+ });
+ pending_tests.add(new Runnable() {
+ public void run() {
+ listener.progress("Running Debugger Attach/Terminate Test...", ++count_done, count_total);
+ for (IChannel channel : channels) {
+ active_tests.put(new TestAttachTerminate(TCFTestSuite.this, run_controls.get(channel), channel), channel);
+ }
+ }
+ });
+ pending_tests.add(new Runnable() {
+ public void run() {
+ listener.progress("Running Path Map Test...", ++count_done, count_total);
+ for (IChannel channel : channels) {
+ active_tests.put(new TestPathMap(TCFTestSuite.this, channel, path_map), channel);
+ }
+ }
+ });
+ pending_tests.add(new Runnable() {
+ public void run() {
+ listener.progress("Running Expressions Test...", ++count_done, count_total);
+ for (IChannel channel : channels) {
+ active_tests.put(new TestExpressions(TCFTestSuite.this, run_controls.get(channel), channel), channel);
+ }
+ }
+ });
+ pending_tests.add(new Runnable() {
+ public void run() {
+ listener.progress("Running Streams Test...", ++count_done, count_total);
+ for (IChannel channel : channels) {
+ active_tests.put(new TestStreams(TCFTestSuite.this, channel), channel);
+ }
+ }
+ });
+ pending_tests.add(new Runnable() {
+ public void run() {
+ listener.progress("Running Sys monitor Test...", ++count_done, count_total);
+ for (IChannel channel : channels) {
+ active_tests.put(new TestSysMonitor(TCFTestSuite.this, channel), channel);
+ }
+ }
+ });
+ pending_tests.add(new Runnable() {
+ public void run() {
+ listener.progress("Running Terminals Test...", ++count_done, count_total);
+ for (IChannel channel : channels) {
+ active_tests.put(new TestTerminals(TCFTestSuite.this, channel), channel);
+ }
+ }
+ });
+ pending_tests.add(new Runnable() {
+ public void run() {
+ int i = 0;
+ listener.progress("Running Run Control Test...", ++count_done, count_total);
+ for (IChannel channel : channels) {
+ active_tests.put(new TestRCBP1(TCFTestSuite.this, run_controls.get(channel),
+ channel, i++, path_map, mem_map), channel);
+ }
+ }
+ });
+ pending_tests.add(new Runnable() {
+ public void run() {
+ int i = 0;
+ listener.progress("Running File System Test...", ++count_done, count_total);
+ for (IChannel channel : channels) {
+ active_tests.put(new TestFileSystem(TCFTestSuite.this, channel, i++), channel);
+ }
+ }
+ });
+ pending_tests.add(new Runnable() {
+ public void run() {
+ listener.progress("Running Interability Test...", ++count_done, count_total);
+ Random rnd = new Random();
+ for (int i = 0; i < channels.length; i++) {
+ IChannel channel = channels[i];
+ ITCFTest test = null;
+ switch (rnd.nextInt(11)) {
+ case 0: test = new TestEcho(TCFTestSuite.this, channel); break;
+ case 1: test = new TestEchoERR(TCFTestSuite.this, channel); break;
+ case 2: test = new TestEchoFP(TCFTestSuite.this, channel); break;
+ case 3: test = new TestAttachTerminate(TCFTestSuite.this, run_controls.get(channel), channel); break;
+ case 4: test = new TestExpressions(TCFTestSuite.this, run_controls.get(channel), channel); break;
+ case 5: test = new TestRCBP1(TCFTestSuite.this, run_controls.get(channel), channel, i, path_map, mem_map); break;
+ case 6: test = new TestFileSystem(TCFTestSuite.this, channel, i); break;
+ case 7: test = new TestPathMap(TCFTestSuite.this, channel, path_map); break;
+ case 8: test = new TestStreams(TCFTestSuite.this, channel); break;
+ case 9: test = new TestSysMonitor(TCFTestSuite.this, channel); break;
+ case 10: test = new TestTerminals(TCFTestSuite.this, channel); break;
+ }
+ active_tests.put(test, channel);
+ }
+ }
+ });
+ count_total = pending_tests.size() * 2;
+ channels = new IChannel[NUM_CHANNELS];
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ try {
+ openChannels(peer);
+ }
+ catch (Throwable x) {
+ errors.add(x);
+ int cnt = 0;
+ for (int i = 0; i < channels.length; i++) {
+ if (channels[i] == null) continue;
+ if (channels[i].getState() != IChannel.STATE_CLOSED) channels[i].close();
+ cnt++;
+ }
+ if (cnt == 0) listener.done(errors);
+ }
+ }
+ });
+ }
+
+ private void openChannels(IPeer peer) {
+ listener.progress("Opening communication channels...", count_done, count_total);
+ for (int i = 0; i < channels.length; i++) {
+ final IChannel channel = channels[i] = peer.openChannel();
+ channel.addChannelListener(new IChannel.IChannelListener() {
+
+ public void onChannelOpened() {
+ for (int i = 0; i < channels.length; i++) {
+ if (channels[i] == null) return;
+ if (channels[i].getState() != IChannel.STATE_OPEN) return;
+ }
+ for (int i = 0; i < channels.length; i++) {
+ run_controls.put(channels[i], new RunControl(TCFTestSuite.this, channels[i], i));
+ }
+ runNextTest();
+ }
+
+ public void congestionLevel(int level) {
+ }
+
+ public void onChannelClosed(Throwable error) {
+ channel.removeChannelListener(this);
+ if (error == null && errors.isEmpty() && (!active_tests.isEmpty() || !pending_tests.isEmpty()) && !cancel) {
+ error = new IOException("Remote peer closed connection before all tests finished");
+ }
+ int cnt = 0;
+ for (int i = 0; i < channels.length; i++) {
+ if (channels[i] == channel) {
+ channels[i] = null;
+ if (error != null && errors.isEmpty()) errors.add(error);
+ for (Iterator<ITCFTest> n = active_tests.keySet().iterator(); n.hasNext();) {
+ if (active_tests.get(n.next()) == channel) n.remove();
+ }
+ }
+ if (channels[i] == null) continue;
+ if ((error != null || active_tests.isEmpty() && pending_tests.isEmpty()) &&
+ channels[i].getState() != IChannel.STATE_CLOSED) channels[i].close();
+ cnt++;
+ }
+ if (cnt == 0) listener.done(errors);
+ }
+ });
+ }
+ }
+
+ public void cancel() {
+ cancel = true;
+ if (canceled) return;
+ for (final ITCFTest t : active_tests.keySet()) {
+ if (t instanceof TestRCBP1) {
+ ((TestRCBP1)t).cancel(new Runnable() {
+ public void run() {
+ assert active_tests.get(t) == null;
+ cancel();
+ }
+ });
+ return;
+ }
+ }
+ canceled = true;
+ for (IChannel c : channels) {
+ if (c != null && c.getState() != IChannel.STATE_CLOSED) c.close();
+ }
+ }
+
+ public boolean isCanceled() {
+ return canceled;
+ }
+
+ boolean isActive(ITCFTest test) {
+ return active_tests.get(test) != null;
+ }
+
+ Collection<ITCFTest> getActiveTests() {
+ return active_tests.keySet();
+ }
+
+ Map<String,String> getCanceledTests() {
+ return cancel_test_ids;
+ }
+
+ boolean canResume(String id) {
+ for (RunControl r : run_controls.values()) {
+ if (!r.canResume(id)) return false;
+ }
+ for (ITCFTest t : active_tests.keySet()) {
+ if (!t.canResume(id)) return false;
+ }
+ return true;
+ }
+
+ void done(ITCFTest test, Throwable error) {
+ assert active_tests.get(test) != null;
+ if (error != null && !canceled) errors.add(error);
+ active_tests.remove(test);
+ if (active_tests.isEmpty()) runNextTest();
+ }
+
+ private void runNextTest() {
+ while (active_tests.isEmpty()) {
+ if (cancel || errors.size() > 0 || pending_tests.size() == 0) {
+ for (IChannel channel : channels) {
+ if (channel != null && channel.getState() != IChannel.STATE_CLOSED) {
+ if (errors.size() > 0) channel.terminate(new Exception("Test failed"));
+ else channel.close();
+ }
+ }
+ return;
+ }
+ listener.progress(null, ++count_done, count_total);
+ pending_tests.removeFirst().run();
+ ITCFTest[] lst = active_tests.keySet().toArray(new ITCFTest[active_tests.size()]);
+ for (ITCFTest test : lst) test.start();
+ }
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestAttachTerminate.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestAttachTerminate.java
new file mode 100644
index 000000000..b258f0035
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestAttachTerminate.java
@@ -0,0 +1,152 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tm.internal.tcf.debug.tests;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Random;
+
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.protocol.Protocol;
+import org.eclipse.tm.tcf.services.IDiagnostics;
+import org.eclipse.tm.tcf.services.IRunControl;
+import org.eclipse.tm.tcf.services.IRunControl.RunControlContext;
+
+class TestAttachTerminate implements ITCFTest, IRunControl.RunControlListener {
+
+ private final TCFTestSuite test_suite;
+ private final RunControl test_rc;
+ private final IDiagnostics diag;
+ private final IRunControl rc;
+ private final Random rnd = new Random();
+
+ private int cnt = 0;
+
+ private final HashSet<String> test_ctx_ids = new HashSet<String>();
+
+ TestAttachTerminate(TCFTestSuite test_suite, RunControl test_rc, IChannel channel) {
+ this.test_suite = test_suite;
+ this.test_rc = test_rc;
+ diag = channel.getRemoteService(IDiagnostics.class);
+ rc = channel.getRemoteService(IRunControl.class);
+ }
+
+ public void start() {
+ if (diag == null || rc == null) {
+ test_suite.done(this, null);
+ }
+ else {
+ rc.addListener(this);
+ diag.getTestList(new IDiagnostics.DoneGetTestList() {
+ public void doneGetTestList(IToken token, Throwable error, String[] list) {
+ if (!test_suite.isActive(TestAttachTerminate.this)) return;
+ if (error != null) {
+ exit(error);
+ }
+ else if (list.length > 0) {
+ startTestContext(list[rnd.nextInt(list.length)]);
+ Protocol.invokeLater(100, new Runnable() {
+ int cnt = 0;
+ public void run() {
+ if (!test_suite.isActive(TestAttachTerminate.this)) return;
+ cnt++;
+ if (test_suite.cancel) {
+ exit(null);
+ }
+ else if (cnt < 300) {
+ Protocol.invokeLater(100, this);
+ }
+ else if (test_ctx_ids.isEmpty()) {
+ exit(new Error("Timeout waiting for 'contextAdded' event"));
+ }
+ else {
+ exit(new Error("Timeout waiting for 'contextRemoved' event. Context: " + test_ctx_ids));
+ }
+ }
+ });
+ return;
+ }
+ exit(null);
+ }
+ });
+ }
+ }
+
+ public boolean canResume(String id) {
+ return true;
+ }
+
+ private void startTestContext(String test_name) {
+ for (int i = 0; i < 4; i++) {
+ diag.runTest(test_name, new IDiagnostics.DoneRunTest() {
+ public void doneRunTest(IToken token, Throwable error, final String id) {
+ cnt--;
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ assert id != null;
+ if (test_rc.getContext(id) == null) {
+ exit(new Error("Missing 'contextAdded' event for context " + id));
+ }
+ else {
+ test_ctx_ids.add(id);
+ test_rc.cancel(id, id);
+ }
+ }
+ }
+ });
+ cnt++;
+ }
+ }
+
+ private void exit(Throwable x) {
+ if (!test_suite.isActive(this)) return;
+ rc.removeListener(this);
+ test_suite.done(this, x);
+ }
+
+ public void containerResumed(String[] context_ids) {
+ for (String id : context_ids) contextResumed(id);
+ }
+
+ public void containerSuspended(String main_context, String pc,
+ String reason, Map<String, Object> params,
+ String[] suspended_ids) {
+ for (String context : suspended_ids) {
+ assert context != null;
+ contextSuspended(context, null, null, null);
+ }
+ }
+
+ public void contextAdded(RunControlContext[] contexts) {
+ }
+
+ public void contextChanged(RunControlContext[] contexts) {
+ }
+
+ public void contextException(String context, String msg) {
+ }
+
+ public void contextRemoved(String[] context_ids) {
+ for (String id : context_ids) {
+ test_ctx_ids.remove(id);
+ }
+ if (cnt == 0 && test_ctx_ids.isEmpty()) exit(null);
+ }
+
+ public void contextResumed(String context) {
+ }
+
+ public void contextSuspended(String context, String pc, String reason, Map<String,Object> params) {
+ }
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestEcho.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestEcho.java
new file mode 100644
index 000000000..eb4a3ff33
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestEcho.java
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tm.internal.tcf.debug.tests;
+
+import java.util.LinkedList;
+
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IErrorReport;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.services.IDiagnostics;
+
+class TestEcho implements ITCFTest, IDiagnostics.DoneEcho {
+
+ private final TCFTestSuite test_suite;
+ private final IDiagnostics diag;
+ private final LinkedList<String> msgs = new LinkedList<String>();
+ private int count = 0;
+ private long start_time;
+
+ TestEcho(TCFTestSuite test_suite, IChannel channel) {
+ this.test_suite = test_suite;
+ diag = channel.getRemoteService(IDiagnostics.class);
+ }
+
+ public void start() {
+ if (diag == null) {
+ test_suite.done(this, null);
+ }
+ else {
+ diag.not_implemented_command(new IDiagnostics.DoneNotImplementedCommand() {
+
+ public void doneNotImplementedCommand(IToken token, Throwable error) {
+ if (!(error instanceof IErrorReport)) {
+ Throwable x = new Exception("Invalid responce to unimplemented command", error);
+ test_suite.done(TestEcho.this, x);
+ return;
+ }
+ if (((IErrorReport)error).getErrorCode() != IErrorReport.TCF_ERROR_INV_COMMAND) {
+ test_suite.done(TestEcho.this, new Exception("Invalid error code in responce to unimplemented command"));
+ return;
+ }
+ start_time = System.currentTimeMillis();
+ for (int i = 0; i < 32; i++) sendMessage();
+ }
+ });
+ }
+ }
+
+ private void sendMessage() {
+ StringBuffer buf = new StringBuffer();
+ buf.append(Integer.toHexString(count));
+ for (int i = 0; i < 64; i++) {
+ buf.append('-');
+ buf.append((char)(0x400 * i + count));
+ }
+ String s = buf.toString();
+ msgs.add(s);
+ diag.echo(s, this);
+ count++;
+ }
+
+ public void doneEcho(IToken token, Throwable error, String b) {
+ String s = msgs.removeFirst();
+ if (!test_suite.isActive(this)) return;
+ if (error != null) {
+ test_suite.done(this, error);
+ }
+ else if (!s.equals(b)) {
+ test_suite.done(this, new Exception("Echo test failed: " + s + " != " + b));
+ }
+ else if (count < 0x400) {
+ sendMessage();
+ // Don't run the test much longer then 4 seconds
+ if (count % 0x10 == 0 && System.currentTimeMillis() - start_time >= 4000) {
+ count = 0x400;
+ }
+ }
+ else if (msgs.isEmpty()){
+ test_suite.done(this, null);
+ }
+ }
+
+ public boolean canResume(String id) {
+ return true;
+ }
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestEchoERR.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestEchoERR.java
new file mode 100644
index 000000000..c855fb90a
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestEchoERR.java
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (c) 2010, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tm.internal.tcf.debug.tests;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+
+import org.eclipse.tm.tcf.core.Command;
+import org.eclipse.tm.tcf.core.ErrorReport;
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IErrorReport;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.services.IDiagnostics;
+
+class TestEchoERR implements ITCFTest, IDiagnostics.DoneEchoERR {
+
+ private final TCFTestSuite test_suite;
+ private final IDiagnostics diag;
+
+ private final Number[] numbers = {
+ 1,
+ 4,
+ new BigDecimal("0.5")
+ };
+
+ private final String[] strings = {
+ "",
+ "abc",
+ "a\u1134c",
+ "a\u0003c",
+ };
+
+ private final String[] formats = {
+ "",
+ "{0}",
+ "{0,number}",
+ "{0,number,integer}",
+ "{0,number,percent}",
+ "{1}",
+ "{0} abcde {1}",
+ "{1} '' {0}",
+ "{1} 'abcde{}' {1}",
+ };
+
+ private final LinkedList<ErrorReport> list = new LinkedList<ErrorReport>();
+
+ TestEchoERR(TCFTestSuite test_suite, IChannel channel) {
+ this.test_suite = test_suite;
+ diag = channel.getRemoteService(IDiagnostics.class);
+ }
+
+ public void start() {
+ for (Number n : numbers) {
+ for (String s : strings) {
+ for (String f : formats) {
+ ArrayList<Object> params = new ArrayList<Object>();
+ params.add(n);
+ params.add(s);
+ Map<String,Object> map = new HashMap<String,Object>();
+ map.put(IErrorReport.ERROR_TIME, new Long(System.currentTimeMillis()));
+ map.put(IErrorReport.ERROR_CODE, new Integer(IErrorReport.TCF_ERROR_OTHER));
+ map.put(IErrorReport.ERROR_FORMAT, f);
+ map.put(IErrorReport.ERROR_PARAMS, params);
+ map.put(s, s); // non-standard attribute
+ ErrorReport e = new ErrorReport("TCF error", map);
+ list.add(e);
+ diag.echoERR(e, this);
+ }
+ }
+ }
+ }
+
+ public void doneEchoERR(IToken token, Throwable error, Throwable error_obj, String error_msg) {
+ ErrorReport e = list.removeFirst();
+ if (!test_suite.isActive(this)) return;
+ Map<String,Object> map0 = e.getAttributes();
+ Map<String,Object> map1 = null;
+
+ if (error_obj instanceof IErrorReport) {
+ map1 = ((IErrorReport)error_obj).getAttributes();
+ }
+
+ String msg = Command.toErrorString(map0);
+
+ if (error instanceof IErrorReport && ((IErrorReport)error).getErrorCode() == IErrorReport.TCF_ERROR_INV_COMMAND) {
+ // Older agent: the command is not available. Just exit the test.
+ test_suite.done(this, null);
+ }
+ else if (error != null) {
+ test_suite.done(this, error);
+ }
+ else if (!map0.equals(map1)) {
+ test_suite.done(this, new Exception("Invalid error report attributes"));
+ }
+ else if (!msg.equals(error_msg)) {
+ test_suite.done(this, new Exception("Invalid error report text"));
+ }
+ else if (list.size() == 0) {
+ test_suite.done(this, null);
+ }
+ }
+
+ public boolean canResume(String id) {
+ return true;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestEchoFP.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestEchoFP.java
new file mode 100644
index 000000000..69e1d3212
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestEchoFP.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2010, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tm.internal.tcf.debug.tests;
+
+import java.math.BigDecimal;
+import java.util.LinkedList;
+import java.util.Random;
+
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.services.IDiagnostics;
+
+class TestEchoFP implements ITCFTest, IDiagnostics.DoneEchoFP {
+
+ private final TCFTestSuite test_suite;
+ private final IDiagnostics diag;
+ private final LinkedList<BigDecimal> msgs = new LinkedList<BigDecimal>();
+ private final Random rnd = new Random();
+
+ private int count = 0;
+ private long start_time;
+
+ TestEchoFP(TCFTestSuite test_suite, IChannel channel) {
+ this.test_suite = test_suite;
+ diag = channel.getRemoteService(IDiagnostics.class);
+ }
+
+ public void start() {
+ if (diag == null) {
+ test_suite.done(this, null);
+ }
+ else {
+ start_time = System.currentTimeMillis();
+ for (int i = 0; i < 32; i++) sendMessage();
+ }
+ }
+
+ private void sendMessage() {
+ BigDecimal n = BigDecimal.valueOf(rnd.nextInt(), rnd.nextInt(61) - 30);
+ msgs.add(n);
+ diag.echoFP(n, this);
+ count++;
+ }
+
+ private boolean cmp(double x, double y) {
+ return (float)x == (float)y;
+ }
+
+ public void doneEchoFP(IToken token, Throwable error, BigDecimal b) {
+ BigDecimal s = msgs.removeFirst();
+ if (!test_suite.isActive(this)) return;
+ if (error != null) {
+ test_suite.done(this, error);
+ }
+ else if (!cmp(s.doubleValue(), b.doubleValue())) {
+ test_suite.done(this, new Exception("EchoFP test failed: " + s + " != " + b));
+ }
+ else if (count < 0x800) {
+ sendMessage();
+ // Don't run the test much longer then 4 seconds
+ if (count % 0x10 == 0 && System.currentTimeMillis() - start_time >= 4000) {
+ count = 0x800;
+ }
+ }
+ else if (msgs.isEmpty()){
+ test_suite.done(this, null);
+ }
+ }
+
+ public boolean canResume(String id) {
+ return true;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestExpressions.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestExpressions.java
new file mode 100644
index 000000000..d59f94b5c
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestExpressions.java
@@ -0,0 +1,650 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tm.internal.tcf.debug.tests;
+
+import java.math.BigInteger;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.protocol.JSON;
+import org.eclipse.tm.tcf.protocol.Protocol;
+import org.eclipse.tm.tcf.services.IBreakpoints;
+import org.eclipse.tm.tcf.services.IDiagnostics;
+import org.eclipse.tm.tcf.services.IExpressions;
+import org.eclipse.tm.tcf.services.IRunControl;
+import org.eclipse.tm.tcf.services.IStackTrace;
+import org.eclipse.tm.tcf.services.ISymbols;
+
+class TestExpressions implements ITCFTest,
+ IRunControl.RunControlListener, IExpressions.ExpressionsListener, IBreakpoints.BreakpointsListener {
+
+ private final TCFTestSuite test_suite;
+ private final RunControl test_rc;
+ private final IDiagnostics diag;
+ private final IExpressions expr;
+ private final ISymbols syms;
+ private final IStackTrace stk;
+ private final IRunControl rc;
+ private final IBreakpoints bp;
+ private final Random rnd = new Random();
+
+ private String test_id;
+ private String bp_id;
+ private boolean bp_ok;
+ private IDiagnostics.ISymbol sym_func3;
+ private String test_ctx_id;
+ private String process_id;
+ private String thread_id;
+ private boolean run_to_bp_done;
+ private boolean test_done;
+ private boolean cancel_test_sent;
+ private IRunControl.RunControlContext test_ctx;
+ private IRunControl.RunControlContext thread_ctx;
+ private String suspended_pc;
+ private boolean waiting_suspend;
+ private String[] stack_trace;
+ private IStackTrace.StackTraceContext[] stack_frames;
+ private String[] local_vars;
+ private final Map<String,IExpressions.Expression> expr_ctx = new HashMap<String,IExpressions.Expression>();
+ private final Map<String,IExpressions.Value> expr_val = new HashMap<String,IExpressions.Value>();
+ private final Map<String,ISymbols.Symbol> expr_sym = new HashMap<String,ISymbols.Symbol>();
+ private final Map<String,String[]> expr_chld = new HashMap<String,String[]>();
+ private final Set<String> expr_to_dispose = new HashSet<String>();
+
+ private static String[] test_expressions = {
+ "func2_local1",
+ "func2_local2",
+ "func2_local3",
+ "func2_local1 == func2_local1",
+ "func2_local1 != func2_local2",
+ "1.34 == 1.34",
+ "1.34 != 1.35",
+ "1 ? 1 : 0",
+ "!func2_local1 ? 0 : 1",
+ "(0 || 0) == 0",
+ "(0 || func2_local1) == 1",
+ "(func2_local1 || 0) == 1",
+ "(func2_local1 || func2_local1) == 1",
+ "(0 && 0) == 0",
+ "(0 && func2_local1) == 0",
+ "(func2_local1 && 0) == 0",
+ "(func2_local1 && func2_local1) == 1",
+ "(func2_local1 | func2_local2) == 3",
+ "(func2_local1 & func2_local2) == 0",
+ "(func2_local1 ^ func2_local2) == 3",
+ "(func2_local1 < func2_local2)",
+ "(func2_local1 <= func2_local2)",
+ "!(func2_local1 > func2_local2)",
+ "!(func2_local1 >= func2_local2)",
+ "(func2_local1 < 1.1)",
+ "(func2_local1 <= 1.1)",
+ "!(func2_local1 > 1.1)",
+ "!(func2_local1 >= 1.1)",
+ "(func2_local2 << 2) == 8",
+ "(func2_local2 >> 1) == 1",
+ "+func2_local2 == 2",
+ "-func2_local2 == -2",
+ "(short)(int)(long)((char *)func2_local2 + 1) == 3",
+ "((func2_local1 + func2_local2) * 2 - 2) / 2 == 2",
+ "func2_local3.f_struct->f_struct->f_struct == &func2_local3",
+ "(char *)func2_local3.f_struct",
+ "(char[4])func2_local3.f_struct",
+ "&((test_struct *)0)->f_float",
+ "&((struct test_struct *)0)->f_float",
+ "tcf_test_func3",
+ "&tcf_test_func3",
+ "tcf_test_array + 10",
+ "*(tcf_test_array + 10) | 1",
+ "&*(char *)(int *)0 == 0",
+ };
+
+ TestExpressions(TCFTestSuite test_suite, RunControl test_rc, IChannel channel) {
+ this.test_suite = test_suite;
+ this.test_rc = test_rc;
+ diag = channel.getRemoteService(IDiagnostics.class);
+ expr = channel.getRemoteService(IExpressions.class);
+ syms = channel.getRemoteService(ISymbols.class);
+ stk = channel.getRemoteService(IStackTrace.class);
+ rc = channel.getRemoteService(IRunControl.class);
+ bp = channel.getRemoteService(IBreakpoints.class);
+ }
+
+ public void start() {
+ if (diag == null || expr == null || stk == null || rc == null || bp == null) {
+ test_suite.done(this, null);
+ }
+ else {
+ expr.addListener(this);
+ rc.addListener(this);
+ bp.addListener(this);
+ diag.getTestList(new IDiagnostics.DoneGetTestList() {
+ public void doneGetTestList(IToken token, Throwable error, String[] list) {
+ if (!test_suite.isActive(TestExpressions.this)) return;
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ if (list.length > 0) {
+ test_id = list[rnd.nextInt(list.length)];
+ runTest();
+ Protocol.invokeLater(100, new Runnable() {
+ int cnt = 0;
+ public void run() {
+ if (!test_suite.isActive(TestExpressions.this)) return;
+ cnt++;
+ if (test_suite.cancel) {
+ exit(null);
+ }
+ else if (cnt < 600) {
+ if (test_done && !cancel_test_sent) {
+ test_rc.cancel(thread_id, test_ctx_id);
+ cancel_test_sent = true;
+ }
+ Protocol.invokeLater(100, this);
+ }
+ else if (test_ctx_id == null) {
+ exit(new Error("Timeout waiting for reply of Diagnostics.runTest command"));
+ }
+ else {
+ exit(new Error("Missing 'contextRemoved' event for " + test_ctx_id));
+ }
+ }
+ });
+ return;
+ }
+ exit(null);
+ }
+ }
+ });
+ }
+ }
+
+ public boolean canResume(String id) {
+ if (test_ctx_id != null && thread_ctx == null) return false;
+ if (thread_ctx != null && !test_done) {
+ assert thread_ctx.getID().equals(thread_id);
+ IRunControl.RunControlContext ctx = test_rc.getContext(id);
+ if (ctx == null) return false;
+ String grp = ctx.getRCGroup();
+ if (id.equals(thread_id) || grp != null && grp.equals(thread_ctx.getRCGroup())) {
+ if (run_to_bp_done) return false;
+ if (sym_func3 == null) return false;
+ if (suspended_pc == null) return false;
+ BigInteger pc0 = JSON.toBigInteger(sym_func3.getValue());
+ BigInteger pc1 = new BigInteger(suspended_pc);
+ if (pc0.equals(pc1)) return false;
+ }
+ }
+ return true;
+ }
+
+ @SuppressWarnings("unchecked")
+ private void runTest() {
+ if (bp_id == null) {
+ bp.set(null, new IBreakpoints.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ bp_id = "TestExpressionsBP";
+ runTest();
+ }
+ }
+ });
+ return;
+ }
+ if (!bp_ok) {
+ Map<String,Object> m = new HashMap<String,Object>();
+ m.put(IBreakpoints.PROP_ID, bp_id);
+ m.put(IBreakpoints.PROP_ENABLED, Boolean.TRUE);
+ m.put(IBreakpoints.PROP_LOCATION, "tcf_test_func3");
+ bp.set(new Map[]{ m }, new IBreakpoints.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ bp_ok = true;
+ runTest();
+ }
+ }
+ });
+ return;
+ }
+ if (test_ctx_id == null) {
+ diag.runTest(test_id, new IDiagnostics.DoneRunTest() {
+ public void doneRunTest(IToken token, Throwable error, String id) {
+ if (error != null) {
+ exit(error);
+ }
+ else if (id == null) {
+ exit(new Exception("Test context ID must not be null"));
+ }
+ else if (test_rc.getContext(id) == null) {
+ exit(new Exception("Missing context added event"));
+ }
+ else {
+ test_ctx_id = id;
+ runTest();
+ }
+ }
+ });
+ return;
+ }
+ if (test_ctx == null) {
+ rc.getContext(test_ctx_id, new IRunControl.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, IRunControl.RunControlContext ctx) {
+ if (error != null) {
+ exit(error);
+ }
+ else if (ctx == null) {
+ exit(new Exception("Invalid test execution context"));
+ }
+ else {
+ test_ctx = ctx;
+ process_id = test_ctx.getProcessID();
+ if (test_ctx.hasState()) thread_id = test_ctx_id;
+ runTest();
+ }
+ }
+ });
+ return;
+ }
+ if (thread_id == null) {
+ rc.getChildren(process_id, new IRunControl.DoneGetChildren() {
+ public void doneGetChildren(IToken token, Exception error, String[] ids) {
+ if (error != null) {
+ exit(error);
+ }
+ else if (ids == null || ids.length == 0) {
+ exit(new Exception("Test process has no threads"));
+ }
+ else if (ids.length != 1) {
+ exit(new Exception("Test process has too many threads"));
+ }
+ else {
+ thread_id = ids[0];
+ runTest();
+ }
+ }
+ });
+ return;
+ }
+ if (thread_ctx == null) {
+ rc.getContext(thread_id, new IRunControl.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, IRunControl.RunControlContext ctx) {
+ if (error != null) {
+ exit(error);
+ }
+ else if (ctx == null || !ctx.hasState()) {
+ exit(new Exception("Invalid thread context"));
+ }
+ else {
+ thread_ctx = ctx;
+ runTest();
+ }
+ }
+ });
+ return;
+ }
+ if (suspended_pc == null) {
+ thread_ctx.getState(new IRunControl.DoneGetState() {
+ public void doneGetState(IToken token, Exception error,
+ boolean suspended, String pc, String reason,
+ Map<String,Object> params) {
+ if (error != null) {
+ exit(new Exception("Cannot get context state", error));
+ }
+ else if (!suspended) {
+ waiting_suspend = true;
+ }
+ else if (pc == null || pc.length() == 0 || pc.equals("0")) {
+ exit(new Exception("Invalid context PC"));
+ }
+ else {
+ suspended_pc = pc;
+ runTest();
+ }
+ }
+ });
+ return;
+ }
+ if (sym_func3 == null) {
+ diag.getSymbol(process_id, "tcf_test_func3", new IDiagnostics.DoneGetSymbol() {
+ public void doneGetSymbol(IToken token, Throwable error, IDiagnostics.ISymbol symbol) {
+ if (error != null) {
+ exit(error);
+ }
+ else if (symbol == null) {
+ exit(new Exception("Symbol must not be null: tcf_test_func3"));
+ }
+ else {
+ sym_func3 = symbol;
+ runTest();
+ }
+ }
+ });
+ return;
+ }
+ if (!run_to_bp_done) {
+ BigInteger pc0 = JSON.toBigInteger(sym_func3.getValue());
+ BigInteger pc1 = new BigInteger(suspended_pc);
+ if (!pc0.equals(pc1)) {
+ waiting_suspend = true;
+ test_rc.resume(thread_id, IRunControl.RM_RESUME);
+ return;
+ }
+ run_to_bp_done = true;
+ }
+ assert test_done || !canResume(thread_id);
+ if (stack_trace == null) {
+ stk.getChildren(thread_id, new IStackTrace.DoneGetChildren() {
+ public void doneGetChildren(IToken token, Exception error, String[] context_ids) {
+ if (error != null) {
+ exit(error);
+ }
+ else if (context_ids == null || context_ids.length < 2) {
+ exit(new Exception("Invalid stack trace"));
+ }
+ else {
+ stack_trace = context_ids;
+ runTest();
+ }
+ }
+ });
+ return;
+ }
+ if (stack_frames == null) {
+ stk.getContext(stack_trace, new IStackTrace.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, IStackTrace.StackTraceContext[] frames) {
+ if (error != null) {
+ exit(error);
+ }
+ else if (frames == null || frames.length != stack_trace.length) {
+ exit(new Exception("Invalid stack trace"));
+ }
+ else {
+ stack_frames = frames;
+ runTest();
+ }
+ }
+ });
+ return;
+ }
+ if (local_vars == null) {
+ expr.getChildren(stack_trace[stack_trace.length - 2], new IExpressions.DoneGetChildren() {
+ public void doneGetChildren(IToken token, Exception error, String[] context_ids) {
+ if (error != null || context_ids == null) {
+ // Need to continue tests even if local variables info is not available.
+ // TODO: need to distinguish absence of debug info from other errors.
+ local_vars = new String[0];
+ runTest();
+ }
+ else {
+ local_vars = context_ids;
+ runTest();
+ }
+ }
+ });
+ return;
+ }
+ for (final String id : local_vars) {
+ if (expr_ctx.get(id) == null) {
+ expr.getContext(id, new IExpressions.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, IExpressions.Expression ctx) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ expr_ctx.put(id, ctx);
+ runTest();
+ }
+ }
+ });
+ return;
+ }
+ }
+ for (final String txt : test_expressions) {
+ if (local_vars.length == 0) {
+ // Debug info not available
+ if (txt.indexOf("local") >= 0) continue;
+ if (txt.indexOf("test_struct") >= 0) continue;
+ if (txt.indexOf("tcf_test_array") >= 0) continue;
+ if (txt.indexOf("(char *)") >= 0) continue;
+ }
+ if (expr_ctx.get(txt) == null) {
+ expr.create(stack_trace[stack_trace.length - 2], null, txt, new IExpressions.DoneCreate() {
+ public void doneCreate(IToken token, Exception error, IExpressions.Expression ctx) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ expr_to_dispose.add(ctx.getID());
+ expr_ctx.put(txt, ctx);
+ runTest();
+ }
+ }
+ });
+ return;
+ }
+ }
+ for (final String id : local_vars) {
+ if (expr_val.get(id) == null) {
+ expr.evaluate(id, new IExpressions.DoneEvaluate() {
+ public void doneEvaluate(IToken token, Exception error, IExpressions.Value ctx) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ expr_val.put(id, ctx);
+ runTest();
+ }
+ }
+ });
+ return;
+ }
+ }
+ for (final String id : expr_ctx.keySet()) {
+ if (expr_val.get(id) == null) {
+ expr.evaluate(expr_ctx.get(id).getID(), new IExpressions.DoneEvaluate() {
+ public void doneEvaluate(IToken token, Exception error, IExpressions.Value ctx) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ expr_val.put(id, ctx);
+ byte[] arr = ctx.getValue();
+ boolean b = false;
+ for (byte x : arr) {
+ if (x != 0) b = true;
+ }
+ if (!b) exit(new Exception("Invalid value of expression \"" + id + "\""));
+ runTest();
+ }
+ }
+ });
+ return;
+ }
+ }
+ if (syms != null) {
+ for (final String id : expr_val.keySet()) {
+ if (expr_sym.get(id) == null) {
+ IExpressions.Value v = expr_val.get(id);
+ String type_id = v.getTypeID();
+ if (type_id != null) {
+ syms.getContext(type_id, new ISymbols.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, ISymbols.Symbol ctx) {
+ if (error != null) {
+ exit(error);
+ }
+ else if (ctx == null) {
+ exit(new Exception("Symbol.getContext returned null"));
+ }
+ else {
+ expr_sym.put(id, ctx);
+ runTest();
+ }
+ }
+ });
+ return;
+ }
+ }
+ }
+ for (final String id : expr_sym.keySet()) {
+ if (expr_chld.get(id) == null) {
+ ISymbols.Symbol sym = expr_sym.get(id);
+ syms.getChildren(sym.getID(), new ISymbols.DoneGetChildren() {
+ public void doneGetChildren(IToken token, Exception error, String[] context_ids) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ if (context_ids == null) context_ids = new String[0];
+ expr_chld.put(id, context_ids);
+ runTest();
+ }
+ }
+ });
+ return;
+ }
+ }
+ }
+ for (final String id : expr_to_dispose) {
+ expr.dispose(id, new IExpressions.DoneDispose() {
+ public void doneDispose(IToken token, Exception error) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ expr_to_dispose.remove(id);
+ runTest();
+ }
+ }
+ });
+ return;
+ }
+ test_done = true;
+ test_rc.resume(thread_id, IRunControl.RM_RESUME);
+ }
+
+ private void exit(Throwable x) {
+ if (!test_suite.isActive(this)) return;
+ expr.removeListener(this);
+ bp.removeListener(this);
+ rc.removeListener(this);
+ test_suite.done(this, x);
+ }
+
+ //--------------------------- Run Control listener ---------------------------//
+
+ public void containerResumed(String[] context_ids) {
+ for (String id : context_ids) contextResumed(id);
+ }
+
+ public void containerSuspended(String context, String pc, String reason,
+ Map<String,Object> params, String[] suspended_ids) {
+ for (String id : suspended_ids) {
+ contextSuspended(id, null, null, null);
+ }
+ }
+
+ public void contextAdded(IRunControl.RunControlContext[] contexts) {
+ }
+
+ public void contextChanged(IRunControl.RunControlContext[] contexts) {
+ }
+
+ public void contextException(String context, String msg) {
+ if (test_done) return;
+ IRunControl.RunControlContext ctx = test_rc.getContext(context);
+ if (ctx != null) {
+ String p = ctx.getParentID();
+ String c = ctx.getCreatorID();
+ if (!test_ctx_id.equals(c) && !test_ctx_id.equals(p)) return;
+ }
+ exit(new Exception("Context exception: " + msg));
+ }
+
+ public void contextRemoved(String[] context_ids) {
+ for (String id : context_ids) {
+ if (id.equals(test_ctx_id)) {
+ if (test_done) {
+ bp.set(null, new IBreakpoints.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ exit(error);
+ }
+ });
+ }
+ else {
+ exit(new Exception("Test process exited too soon"));
+ }
+ return;
+ }
+ }
+ }
+
+ public void contextResumed(String id) {
+ if (id.equals(thread_id)) {
+ if (run_to_bp_done && !test_done) {
+ assert thread_ctx != null;
+ assert !canResume(thread_id);
+ exit(new Exception("Unexpected contextResumed event: " + id));
+ }
+ suspended_pc = null;
+ }
+ }
+
+ public void contextSuspended(String id, String pc, String reason, Map<String,Object> params) {
+ assert id != null;
+ if (id.equals(thread_id) && waiting_suspend) {
+ suspended_pc = pc;
+ waiting_suspend = false;
+ runTest();
+ }
+ }
+
+ //--------------------------- Expressions listener ---------------------------//
+
+ public void valueChanged(String id) {
+ }
+
+ //--------------------------- Breakpoints listener ---------------------------//
+
+ @SuppressWarnings("unchecked")
+ public void breakpointStatusChanged(String id, Map<String,Object> status) {
+ if (id.equals(bp_id) && process_id != null && !test_done) {
+ String s = (String)status.get(IBreakpoints.STATUS_ERROR);
+ if (s != null) exit(new Exception("Invalid BP status: " + s));
+ Collection<Map<String,Object>> list = (Collection<Map<String,Object>>)status.get(IBreakpoints.STATUS_INSTANCES);
+ if (list == null) return;
+ String err = null;
+ for (Map<String,Object> map : list) {
+ String ctx = (String)map.get(IBreakpoints.INSTANCE_CONTEXT);
+ if (process_id.equals(ctx) && map.get(IBreakpoints.INSTANCE_ERROR) != null)
+ err = (String)map.get(IBreakpoints.INSTANCE_ERROR);
+ }
+ if (err != null) exit(new Exception("Invalid BP status: " + err));
+ }
+ }
+
+ public void contextAdded(Map<String,Object>[] bps) {
+ }
+
+ public void contextChanged(Map<String,Object>[] bps) {
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestFileSystem.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestFileSystem.java
new file mode 100644
index 000000000..7c601e7e2
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestFileSystem.java
@@ -0,0 +1,482 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tm.internal.tcf.debug.tests;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Random;
+
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.protocol.Protocol;
+import org.eclipse.tm.tcf.services.IFileSystem;
+import org.eclipse.tm.tcf.services.IFileSystem.DirEntry;
+import org.eclipse.tm.tcf.services.IFileSystem.FileAttrs;
+import org.eclipse.tm.tcf.services.IFileSystem.FileSystemException;
+import org.eclipse.tm.tcf.services.IFileSystem.IFileHandle;
+import org.eclipse.tm.tcf.util.TCFFileInputStream;
+import org.eclipse.tm.tcf.util.TCFFileOutputStream;
+
+class TestFileSystem implements ITCFTest, IFileSystem.DoneStat,
+ IFileSystem.DoneOpen, IFileSystem.DoneClose,
+ IFileSystem.DoneWrite, IFileSystem.DoneRead,
+ IFileSystem.DoneRename, IFileSystem.DoneRealPath,
+ IFileSystem.DoneRemove, IFileSystem.DoneRoots,
+ IFileSystem.DoneReadDir {
+
+ private final TCFTestSuite test_suite;
+ private final int channel_id;
+
+ private static final int
+ STATE_PRE = 0,
+ STATE_RD_DIR = 1,
+ STATE_WRITE = 2,
+ STATE_READ = 3,
+ STATE_OUT = 4,
+ STATE_INP = 5,
+ STATE_EXIT = 6;
+
+ private final IFileSystem files;
+ private final Random rnd = new Random();
+ private final LinkedList<String> tmp_files = new LinkedList<String>();
+ private final HashMap<IToken,Object> cmds = new HashMap<IToken,Object>();
+ private byte[] data;
+ private String root;
+ private String tmp_path;
+ private String file_name;
+ private IFileHandle handle;
+ private int state = STATE_PRE;
+ private boolean async_close;
+
+ private static class ReadCmd {
+ int offs;
+ int size;
+ }
+
+ TestFileSystem(TCFTestSuite test_suite, IChannel channel, int channel_id) {
+ this.test_suite = test_suite;
+ this.channel_id = channel_id;
+ files = channel.getRemoteService(IFileSystem.class);
+ }
+
+ public void start() {
+ if (files == null) {
+ test_suite.done(this, null);
+ }
+ else {
+ files.roots(this);
+ }
+ }
+
+ public void doneRoots(IToken token, FileSystemException error, DirEntry[] entries) {
+ assert state == STATE_PRE;
+ if (error != null) {
+ exit(error);
+ }
+ else if (entries == null || entries.length == 0) {
+ exit(new Exception("Invalid FileSysrem.roots responce: empty roots array"));
+ }
+ else {
+ for (DirEntry d : entries) {
+ if (d.filename.startsWith("A:")) continue;
+ if (d.filename.startsWith("B:")) continue;
+ root = d.filename;
+ break;
+ }
+ if (root == null) exit(new Exception("Invalid FileSystem.roots responce: no suitable root"));
+ else files.opendir(root, this);
+ }
+ }
+
+ public void doneReadDir(IToken token, FileSystemException error,
+ DirEntry[] entries, boolean eof) {
+ if (error != null) {
+ exit(error);
+ }
+ else if (state == STATE_PRE) {
+ if (entries != null && tmp_path == null) {
+ for (DirEntry e : entries) {
+ if (e.filename.equals("tmp") || e.filename.equalsIgnoreCase("temp")) {
+ tmp_path = root;
+ if (!tmp_path.endsWith("/")) tmp_path += "/";
+ tmp_path += e.filename;
+ break;
+ }
+ }
+ }
+ if (eof) {
+ if (tmp_path == null) {
+ exit(new Exception("File system test filed: cannot find temporary directory"));
+ return;
+ }
+ files.close(handle, this);
+ }
+ else {
+ files.readdir(handle, this);
+ }
+ }
+ else if (state == STATE_RD_DIR) {
+ if (entries != null) {
+ for (DirEntry e : entries) {
+ if (e.filename.startsWith("tcf-test-" + channel_id + "-")) {
+ tmp_files.add(e.filename);
+ }
+ }
+ }
+ if (eof) {
+ files.close(handle, this);
+ }
+ else {
+ files.readdir(handle, this);
+ }
+ }
+ else {
+ assert false;
+ }
+ }
+
+ public void doneStat(IToken token, FileSystemException error, FileAttrs attrs) {
+ if (error != null) {
+ exit(error);
+ }
+ else if (state == STATE_READ) {
+ if (attrs.size != data.length) {
+ exit(new Exception("Invalid FileSysrem.fstat responce: wrong file size"));
+ }
+ else {
+ files.close(handle, this);
+ }
+ }
+ else if (state == STATE_WRITE) {
+ char[] bf = new char[64];
+ for (int i = 0; i < bf.length; i++) {
+ char ch = (char)(rnd.nextInt(0x4000) + 0x20);
+ switch (ch) {
+ case '<':
+ case '>':
+ case ':':
+ case '"':
+ case '/':
+ case '\\':
+ case '|':
+ case '?':
+ case '*':
+ case '~':
+ ch = '-';
+ break;
+ }
+ bf[i] = ch;
+ }
+ file_name = tmp_path + "/tcf-test-" + channel_id + "-" + new String(bf) + ".tmp";
+ files.open(file_name, IFileSystem.TCF_O_CREAT | IFileSystem.TCF_O_TRUNC | IFileSystem.TCF_O_WRITE, null, this);
+ }
+ else {
+ assert false;
+ }
+ }
+
+ public void doneOpen(IToken token, FileSystemException error, final IFileHandle handle) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ this.handle = handle;
+ if (state == STATE_READ) {
+ for (int i = 0; i < rnd.nextInt(8); i++) {
+ ReadCmd cmd = new ReadCmd();
+ cmd.offs = rnd.nextInt(data.length);
+ cmd.size = rnd.nextInt(data.length - cmd.offs) + 2;
+ cmds.put(files.read(handle, cmd.offs, cmd.size, this), cmd);
+ }
+ if (rnd.nextBoolean()) {
+ ReadCmd cmd = new ReadCmd();
+ cmd.offs = 0;
+ cmd.size = data.length + 1;
+ cmds.put(files.read(handle, cmd.offs, cmd.size, this), cmd);
+ }
+ else {
+ int pos = 0;
+ while (pos < data.length) {
+ int size = rnd.nextInt(data.length - pos) + 1;
+ ReadCmd cmd = new ReadCmd();
+ cmd.offs = pos;
+ cmd.size = size + 1;
+ cmds.put(files.read(handle, cmd.offs, cmd.size, this), cmd);
+ pos += size;
+ }
+ }
+ async_close = rnd.nextBoolean();
+ if (async_close) files.close(handle, this);
+ }
+ else if (state == STATE_WRITE) {
+ data = new byte[rnd.nextInt(0x1000) + 1];
+ rnd.nextBytes(data);
+ if (rnd.nextBoolean()) {
+ cmds.put(files.write(handle, 0, data, 0, data.length, this), null);
+ }
+ else {
+ int pos = 0;
+ while (pos < data.length) {
+ int size = rnd.nextInt(data.length - pos) + 1;
+ cmds.put(files.write(handle, pos, data, pos, size, this), null);
+ pos += size;
+ }
+ }
+ async_close = rnd.nextBoolean();
+ if (async_close) files.close(handle, this);
+ }
+ else if (state == STATE_INP) {
+ Thread thread = new Thread() {
+ public void run() {
+ try {
+ int pos = 0;
+ int len = data.length * 16;
+ byte[] buf = new byte[333];
+ int buf_pos = 0;
+ int buf_len = 0;
+ boolean mark = true;
+ boolean reset = true;
+ int mark_pos = rnd.nextInt(len - 1);
+ int reset_pos = mark_pos + rnd.nextInt(len - mark_pos);
+ assert reset_pos >= mark_pos;
+ InputStream inp = new TCFFileInputStream(handle, 133);
+ for (;;) {
+ if (mark && pos == mark_pos) {
+ inp.mark(len);
+ mark = false;
+ }
+ if (reset && pos == reset_pos) {
+ inp.reset();
+ reset = false;
+ pos = mark_pos;
+ buf_pos = buf_len = 0;
+ }
+ int ch = 0;
+ if (buf_pos >= buf_len && (pos >= mark_pos || pos + buf.length <= mark_pos)) {
+ buf_len = inp.read(buf, buf_pos = 0, buf.length);
+ if (buf_len < 0) break;
+ }
+ if (buf_pos < buf_len) {
+ ch = buf[buf_pos++] & 0xff;
+ }
+ else {
+ ch = inp.read();
+ if (ch < 0) break;
+ }
+ int dt = data[pos % data.length] & 0xff;
+ if (ch != dt) {
+ error(new Exception("Invalid TCFFileInputStream.read responce: wrong data at offset " + pos +
+ ", expected " + dt + ", actual " + ch));
+ }
+ pos++;
+ }
+ if (pos != data.length * 16) {
+ error(new Exception("Invalid TCFFileInputStream.read responce: wrong file length: " +
+ "expected " + data.length + ", actual " + pos));
+ }
+ inp.close();
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ state = STATE_EXIT;
+ files.rename(file_name, file_name + ".rnm", TestFileSystem.this);
+ }
+ });
+ }
+ catch (Throwable x) {
+ error(x);
+ }
+ }
+ private void error(final Throwable x) {
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ exit(x);
+ }
+ });
+ }
+ };
+ thread.setName("TCF FileSystem Test");
+ thread.start();
+ }
+ else if (state == STATE_OUT) {
+ rnd.nextBytes(data);
+ Thread thread = new Thread() {
+ public void run() {
+ try {
+ int pos = 0;
+ int len = data.length * 16;
+ OutputStream out = new TCFFileOutputStream(handle, 121);
+ while (pos < len) {
+ int m = pos % data.length;
+ int n = rnd.nextInt(1021);
+ if (n > data.length - m) n = data.length - m;
+ out.write(data, m, n);
+ pos += n;
+ if (pos == len) break;
+ out.write(data[pos % data.length] & 0xff);
+ pos++;
+ }
+ out.close();
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ state = STATE_INP;
+ files.open(file_name, IFileSystem.TCF_O_READ, null, TestFileSystem.this);
+ }
+ });
+ }
+ catch (Throwable x) {
+ error(x);
+ }
+ }
+ private void error(final Throwable x) {
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ exit(x);
+ }
+ });
+ }
+ };
+ thread.setName("TCF FileSystem Test");
+ thread.start();
+ }
+ else {
+ assert state == STATE_PRE || state == STATE_RD_DIR;
+ files.readdir(handle, this);
+ }
+ }
+ }
+
+ public void doneWrite(IToken token, FileSystemException error) {
+ assert cmds.containsKey(token);
+ cmds.remove(token);
+ if (error != null) {
+ exit(error);
+ }
+ else if (cmds.size() == 0 && !async_close) {
+ files.close(handle, this);
+ }
+ }
+
+ public void doneRead(IToken token, FileSystemException error, byte[] data, boolean eof) {
+ assert cmds.containsKey(token);
+ ReadCmd cmd = (ReadCmd)cmds.remove(token);
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ if (cmd.offs + cmd.size > this.data.length && !eof) {
+ exit(new Exception("Invalid FileSysrem.read responce: EOF expected"));
+ }
+ else if (data.length != (eof ? cmd.size - 1 : cmd.size)) {
+ exit(new Exception("Invalid FileSysrem.read responce: wrong data array size"));
+ }
+ else {
+ for (int i = 0; i < data.length; i++) {
+ if (data[i] != this.data[i + cmd.offs]) {
+ exit(new Exception("Invalid FileSysrem.read responce: wrong data at offset " + i +
+ ", expected " + this.data[i + cmd.offs] + ", actual " + data[i]));
+ return;
+ }
+ }
+ if (cmds.size() == 0 && !async_close) files.fstat(handle, this);
+ }
+ }
+ }
+
+ public void doneClose(IToken token, FileSystemException error) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ handle = null;
+ if (state == STATE_PRE) {
+ state = STATE_RD_DIR;
+ files.opendir(tmp_path, this);
+ }
+ else if (state == STATE_RD_DIR) {
+ state = STATE_WRITE;
+ files.realpath(tmp_path, this);
+ }
+ else if (state == STATE_WRITE) {
+ state = STATE_READ;
+ files.open(file_name, IFileSystem.TCF_O_READ, null, this);
+ }
+ else if (state == STATE_READ) {
+ state = STATE_OUT;
+ files.open(file_name, IFileSystem.TCF_O_WRITE, null, this);
+ }
+ else {
+ assert false;
+ }
+ }
+ }
+
+ public void doneRename(IToken token, FileSystemException error) {
+ assert state == STATE_EXIT;
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ files.realpath(file_name + ".rnm", this);
+ }
+ }
+
+ public void doneRealPath(IToken token, FileSystemException error, String path) {
+ if (error != null) {
+ exit(error);
+ }
+ else if (state == STATE_WRITE) {
+ tmp_path = path;
+ if (tmp_files.size() > 0) {
+ files.remove(tmp_path + "/" + tmp_files.removeFirst(), this);
+ }
+ else {
+ files.stat(tmp_path, this);
+ }
+ }
+ else if (!path.equals(file_name + ".rnm")) {
+ exit(new Exception("Invalid FileSysrem.realpath responce: " + path));
+ }
+ else {
+ files.remove(file_name + ".rnm", this);
+ }
+ }
+
+ public void doneRemove(IToken token, FileSystemException error) {
+ if (error != null) {
+ exit(error);
+ }
+ else if (state == STATE_WRITE) {
+ if (tmp_files.size() > 0) {
+ files.remove(tmp_path + "/" + tmp_files.removeFirst(), this);
+ }
+ else {
+ files.stat(tmp_path, this);
+ }
+ }
+ else if (state == STATE_EXIT) {
+ exit(null);
+ }
+ else {
+ assert false;
+ }
+ }
+
+ private void exit(Throwable x) {
+ if (!test_suite.isActive(this)) return;
+ test_suite.done(this, x);
+ }
+
+ public boolean canResume(String id) {
+ return true;
+ }
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestPathMap.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestPathMap.java
new file mode 100644
index 000000000..c120565de
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestPathMap.java
@@ -0,0 +1,182 @@
+/*******************************************************************************
+ * Copyright (c) 2010, 1011 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.tests;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.services.IPathMap;
+import org.eclipse.tm.tcf.services.IPathMap.PathMapRule;
+
+class TestPathMap implements ITCFTest {
+
+ private final TCFTestSuite test_suite;
+ private final List<PathMapRule> map;
+ private final IPathMap service;
+
+ private final Random rnd = new Random();
+
+ private static final String[] prop_names = {
+ IPathMap.PROP_SOURCE,
+ IPathMap.PROP_DESTINATION,
+ IPathMap.PROP_HOST,
+ IPathMap.PROP_PROTOCOL,
+ };
+
+ private int cnt = 0;
+
+ private static class Rule implements IPathMap.PathMapRule {
+
+ final Map<String,Object> props;
+
+ public Rule(Map<String,Object> props) {
+ this.props = props;
+ }
+
+ public Map<String,Object> getProperties() {
+ return props;
+ }
+
+ public String getID() {
+ return (String)props.get(IPathMap.PROP_ID);
+ }
+
+ public String getSource() {
+ return (String)props.get(IPathMap.PROP_SOURCE);
+ }
+
+ public String getDestination() {
+ return (String)props.get(IPathMap.PROP_DESTINATION);
+ }
+
+ public String getHost() {
+ return (String)props.get(IPathMap.PROP_HOST);
+ }
+
+ public String getProtocol() {
+ return (String)props.get(IPathMap.PROP_PROTOCOL);
+ }
+
+ public String toString() {
+ return props.toString();
+ }
+ }
+
+ TestPathMap(TCFTestSuite test_suite, IChannel channel, List<PathMapRule> map) {
+ this.test_suite = test_suite;
+ this.map = map;
+ service = channel.getRemoteService(IPathMap.class);
+ }
+
+ public void start() {
+ if (service == null) {
+ exit(null);
+ }
+ else {
+ test_map();
+ }
+ }
+
+ private void test_map() {
+ if (cnt >= 40) {
+ if (map == null) {
+ exit(null);
+ }
+ else {
+ service.set(map.toArray(new PathMapRule[map.size()]), new IPathMap.DoneSet() {
+ public void doneSet(IToken token, Exception error) {
+ exit(error);
+ }
+ });
+ }
+ }
+ else {
+ cnt++;
+ final IPathMap.PathMapRule[] map_out = new IPathMap.PathMapRule[rnd.nextInt(12)];
+ for (int i = 0; i < map_out.length; i++) {
+ Map<String,Object> props = new HashMap<String,Object>();
+ props.put(IPathMap.PROP_ID, "PM" + i);
+ for (int l = 0; l < 2; l++) {
+ String nm = prop_names[rnd.nextInt(prop_names.length)];
+ StringBuffer bf = new StringBuffer();
+ int n = rnd.nextInt(1024);
+ for (int j = 0; j < n; j++) {
+ char ch = (char)(rnd.nextInt(0xfff0) + 1);
+ bf.append(ch);
+ }
+ String val = bf.toString();
+ props.put(nm, val);
+ }
+ map_out[i] = new Rule(props);
+ }
+ service.set(map_out, new IPathMap.DoneSet() {
+ public void doneSet(IToken token, Exception error) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ service.get(new IPathMap.DoneGet() {
+ public void doneGet(IToken token, Exception error, PathMapRule[] map_inp) {
+ if (error != null) {
+ exit(error);
+ }
+ else if (map_inp == null) {
+ exit(new Exception("PathMap.get returned null"));
+ }
+ else if (map_out.length != map_inp.length) {
+ exit(new Exception("PathMap.get error: wrong map size"));
+ }
+ else {
+ for (int i = 0; i < map_out.length; i++) {
+ if (!map_equ(map_out[i].getProperties(), map_inp[i].getProperties())) {
+ return;
+ }
+ }
+ test_map();
+ }
+ }
+ });
+ }
+ }
+ });
+ }
+ }
+
+ private boolean map_equ(Map<String,Object> x, Map<String,Object> y) {
+ for (String key : x.keySet()) {
+ if (!obj_equ(key, x.get(key), y.get(key))) return false;
+ }
+ for (String key : y.keySet()) {
+ if (!obj_equ(key, x.get(key), y.get(key))) return false;
+ }
+ return true;
+ }
+
+ private boolean obj_equ(String nm, Object x, Object y) {
+ if (x == y) return true;
+ if (x != null && x.equals(y)) return true;
+ exit(new Exception("PathMap.get: wrong map data, " + nm + ": " + x + " != " + y));
+ return false;
+ }
+
+ private void exit(Throwable x) {
+ if (!test_suite.isActive(this)) return;
+ test_suite.done(this, x);
+ }
+
+ public boolean canResume(String id) {
+ return false;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestRCBP1.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestRCBP1.java
new file mode 100644
index 000000000..56ed77663
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestRCBP1.java
@@ -0,0 +1,1729 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tm.internal.tcf.debug.tests;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IErrorReport;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.protocol.JSON;
+import org.eclipse.tm.tcf.protocol.Protocol;
+import org.eclipse.tm.tcf.services.IBreakpoints;
+import org.eclipse.tm.tcf.services.IDiagnostics;
+import org.eclipse.tm.tcf.services.IDisassembly;
+import org.eclipse.tm.tcf.services.ILineNumbers;
+import org.eclipse.tm.tcf.services.IMemory;
+import org.eclipse.tm.tcf.services.IMemoryMap;
+import org.eclipse.tm.tcf.services.IPathMap;
+import org.eclipse.tm.tcf.services.IRegisters;
+import org.eclipse.tm.tcf.services.IRunControl;
+import org.eclipse.tm.tcf.services.ISymbols;
+import org.eclipse.tm.tcf.services.IDiagnostics.ISymbol;
+import org.eclipse.tm.tcf.services.IDisassembly.IDisassemblyLine;
+import org.eclipse.tm.tcf.services.ILineNumbers.CodeArea;
+import org.eclipse.tm.tcf.services.IMemory.MemoryContext;
+import org.eclipse.tm.tcf.services.IMemory.MemoryError;
+import org.eclipse.tm.tcf.services.IMemoryMap.MemoryRegion;
+import org.eclipse.tm.tcf.services.IPathMap.PathMapRule;
+import org.eclipse.tm.tcf.services.IRegisters.RegistersContext;
+import org.eclipse.tm.tcf.services.IRunControl.RunControlContext;
+import org.eclipse.tm.tcf.services.ISymbols.Symbol;
+
+class TestRCBP1 implements ITCFTest, IRunControl.RunControlListener {
+
+ private final TCFTestSuite test_suite;
+ private final RunControl test_rc;
+ private final int channel_id;
+ private final List<PathMapRule> path_map;
+ private final Map<String,ArrayList<MemoryRegion>> mem_map;
+ private final IDiagnostics srv_diag;
+ private final ISymbols srv_syms;
+ private final IMemory srv_memory;
+ private final IRunControl srv_run_ctrl;
+ private final IRegisters srv_registers;
+ private final IBreakpoints srv_breakpoints;
+ private final ILineNumbers srv_line_numbers;
+ private final IDisassembly srv_disassembly;
+ private final IPathMap srv_path_map;
+ private final IMemoryMap srv_memory_map;
+ private final Map<String,IRunControl.RunControlContext> threads = new HashMap<String,IRunControl.RunControlContext>();
+ private final Map<String,SuspendedContext> suspended = new HashMap<String,SuspendedContext>();
+ private final Map<String,SuspendedContext> suspended_prev = new HashMap<String,SuspendedContext>();
+ private final Map<String,IDisassemblyLine[]> disassembly_lines = new HashMap<String,IDisassemblyLine[]>();
+ private final Map<String,Map<String,Object>[]> disassembly_capabilities = new HashMap<String,Map<String,Object>[]>();
+ private final Set<String> running = new HashSet<String>();
+ private final Set<IToken> get_state_cmds = new HashSet<IToken>();
+ private final Map<String,Map<String,IRegisters.RegistersContext>> regs =
+ new HashMap<String,Map<String,IRegisters.RegistersContext>>();
+ private final Map<String,Map<String,Object>> bp_list = new HashMap<String,Map<String,Object>>();
+ private final Map<String,IDiagnostics.ISymbol> sym_list = new HashMap<String,IDiagnostics.ISymbol>();
+ private final Random rnd = new Random();
+
+ private String[] test_list;
+ private String test_id;
+ private boolean path_map_done;
+ private boolean mem_map_done;
+ private String test_ctx_id; // Test context ID
+ private IRunControl.RunControlContext test_context;
+ private String main_thread_id;
+ private Map<String,Object> bp_capabilities;
+ private Runnable pending_cancel;
+ private int bp_cnt;
+ private boolean done_get_state;
+ private boolean done_disassembly;
+ private int resume_cnt = 0;
+ private IToken cancel_test_cmd;
+ private boolean bp_reset_done;
+ private boolean bp_set_done;
+ private boolean bp_change_done;
+ private boolean bp_sync_done;
+ private boolean data_bp_area_done;
+ private ILineNumbers.CodeArea data_bp_area;
+ private String data_bp_id;
+ private int data_bp_cnt;
+ private boolean mem_map_test_running;
+ private boolean mem_map_test_done;
+ private boolean all_setup_done;
+
+ private static int mem_map_region_id = 0;
+
+ private static class SuspendedContext {
+ final String id;
+ final String pc;
+ final String reason;
+ final Map<String,Object> params;
+
+ boolean get_state_pending;
+ boolean ok_to_resume;
+
+ SuspendedContext(String id, String pc, String reason, Map<String,Object> params) {
+ this.id = id;
+ this.pc = pc;
+ this.reason = reason;
+ this.params = params;
+ }
+ }
+
+ private static class MemRegion implements MemoryRegion {
+
+ private final Map<String,Object> props;
+
+ MemRegion(Map<String,Object> props) {
+ this.props = props;
+ }
+
+ public Number getAddress() {
+ return (Number)props.get(IMemoryMap.PROP_ADDRESS);
+ }
+
+ public Number getSize() {
+ return (Number)props.get(IMemoryMap.PROP_SIZE);
+ }
+
+ public Number getOffset() {
+ return (Number)props.get(IMemoryMap.PROP_OFFSET);
+ }
+
+ public String getFileName() {
+ return (String)props.get(IMemoryMap.PROP_FILE_NAME);
+ }
+
+ public String getSectionName() {
+ return (String)props.get(IMemoryMap.PROP_SECTION_NAME);
+ }
+
+ public int getFlags() {
+ Number n = (Number)props.get(IMemoryMap.PROP_FLAGS);
+ if (n != null) return n.intValue();
+ return 0;
+ }
+
+ public Map<String,Object> getProperties() {
+ return props;
+ }
+ }
+
+ private final IBreakpoints.BreakpointsListener bp_listener = new IBreakpoints.BreakpointsListener() {
+
+ @SuppressWarnings("unchecked")
+ public void breakpointStatusChanged(String id, Map<String,Object> status) {
+ if (bp_list.get(id) != null && test_context != null && bp_cnt < 40) {
+ if (test_context != null) {
+ String prs = test_context.getProcessID();
+ if (prs != null) {
+ for (ITCFTest test : test_suite.getActiveTests()) {
+ if (test instanceof TestRCBP1) {
+ TestRCBP1 rcbp = (TestRCBP1)test;
+ if (!rcbp.mem_map_test_running) continue;
+ if (prs.equals(rcbp.test_context.getProcessID())) return;
+ }
+ }
+ }
+ }
+ String s = (String)status.get(IBreakpoints.STATUS_ERROR);
+ if (s != null) exit(new Exception("Invalid BP status: " + s));
+ Collection<Map<String,Object>> list = (Collection<Map<String,Object>>)status.get(IBreakpoints.STATUS_INSTANCES);
+ if (list == null) return;
+ String err = null;
+ for (Map<String,Object> map : list) {
+ String ctx = (String)map.get(IBreakpoints.INSTANCE_CONTEXT);
+ if (test_context.getProcessID().equals(ctx) && map.get(IBreakpoints.INSTANCE_ERROR) != null)
+ err = (String)map.get(IBreakpoints.INSTANCE_ERROR);
+ }
+ if (err != null) {
+ if (bp_cnt == 0 && id.equals(data_bp_id)) return;
+ exit(new Exception("Invalid BP status: " + err));
+ }
+ }
+ }
+
+ public void contextAdded(Map<String,Object>[] bps) {
+ for (Map<String,Object> m0 : bps) {
+ String id = (String)m0.get(IBreakpoints.PROP_ID);
+ Map<String,Object> m1 = bp_list.get(id);
+ if (!checkBPData(m0, m1)) return;
+ }
+ }
+
+ public void contextChanged(Map<String,Object>[] bps) {
+ for (Map<String,Object> m0 : bps) {
+ String id = (String)m0.get(IBreakpoints.PROP_ID);
+ Map<String,Object> m1 = bp_list.get(id);
+ if (!checkBPData(m0, m1)) return;
+ }
+ }
+
+ public void contextRemoved(String[] ids) {
+ if (!bp_change_done) return;
+ for (String id : ids) {
+ if (bp_list.get(id) != null) {
+ exit(new Exception("Invalid Breakpoints.contextRemoved event"));
+ return;
+ }
+ }
+ }
+
+ private boolean checkBPData(Map<String,Object> m0, Map<String,Object> m1) {
+ if (m1 == null) return true;
+ m0 = new HashMap<String,Object>(m0);
+ if (m0.get(IBreakpoints.PROP_ENABLED) == null) m0.put(IBreakpoints.PROP_ENABLED, Boolean.FALSE);
+ if (m1.get(IBreakpoints.PROP_ENABLED) == null) m1.put(IBreakpoints.PROP_ENABLED, Boolean.FALSE);
+ if (!m1.equals(m0)) {
+ exit(new Exception("Invalid data in Breakpoints event: " + m0 + " != " + m1));
+ return false;
+ }
+ return true;
+ }
+ };
+
+ TestRCBP1(TCFTestSuite test_suite, RunControl test_rc, IChannel channel, int channel_id,
+ List<PathMapRule> path_map, Map<String,ArrayList<MemoryRegion>> mem_map) {
+ this.test_suite = test_suite;
+ this.test_rc = test_rc;
+ this.channel_id = channel_id;
+ this.path_map = path_map;
+ this.mem_map = mem_map;
+ srv_diag = channel.getRemoteService(IDiagnostics.class);
+ srv_syms = channel.getRemoteService(ISymbols.class);
+ srv_memory = channel.getRemoteService(IMemory.class);
+ srv_run_ctrl = channel.getRemoteService(IRunControl.class);
+ srv_registers = channel.getRemoteService(IRegisters.class);
+ srv_breakpoints = channel.getRemoteService(IBreakpoints.class);
+ srv_line_numbers = channel.getRemoteService(ILineNumbers.class);
+ srv_disassembly = channel.getRemoteService(IDisassembly.class);
+ srv_path_map = channel.getRemoteService(IPathMap.class);
+ srv_memory_map = channel.getRemoteService(IMemoryMap.class);
+ }
+
+ public void start() {
+ if (srv_run_ctrl == null) {
+ test_suite.done(this, null);
+ }
+ else {
+ if (srv_breakpoints != null) srv_breakpoints.addListener(bp_listener);
+ runTest();
+ }
+ }
+
+ private void runTest() {
+ if (!test_suite.isActive(this)) return;
+ if (!path_map_done) {
+ setPathMap();
+ return;
+ }
+ if (!mem_map_done) {
+ setMemMap();
+ return;
+ }
+ if (test_list == null) {
+ getTestList();
+ return;
+ }
+ if (!bp_reset_done) {
+ resetBreakpoints();
+ return;
+ }
+ if (test_id != null) {
+ if (test_ctx_id == null) {
+ startTestContext();
+ return;
+ }
+ if (test_context == null) {
+ getTestContext();
+ return;
+ }
+ if (sym_list.isEmpty()) {
+ getSymbols();
+ return;
+ }
+ if (!data_bp_area_done) {
+ getDataBPFile();
+ return;
+ }
+ if (bp_capabilities == null) {
+ getBreakpointCapabilities();
+ return;
+ }
+ if (!bp_set_done) {
+ iniBreakpoints();
+ return;
+ }
+ }
+ if (!done_get_state) {
+ assert get_state_cmds.isEmpty();
+ assert threads.isEmpty();
+ assert running.isEmpty();
+ assert suspended.isEmpty();
+ getContextState(test_ctx_id);
+ return;
+ }
+ if (srv_disassembly != null && !done_disassembly) {
+ assert get_state_cmds.isEmpty();
+ assert disassembly_lines.isEmpty();
+ getDisassemlyLines();
+ return;
+ }
+ if (test_id != null) {
+ if (!bp_change_done) {
+ changeBreakpoints();
+ return;
+ }
+ if (!mem_map_test_done) {
+ runMemoryMapTest();
+ return;
+ }
+ assert resume_cnt == 0;
+ assert !all_setup_done;
+ all_setup_done = true;
+ for (SuspendedContext s : suspended.values()) resume(s.id);
+ }
+ else if (suspended.size() > 0) {
+ final int test_cnt = suspended.size();
+ Runnable done = new Runnable() {
+ int done_cnt;
+ public void run() {
+ done_cnt++;
+ if (done_cnt == test_cnt) {
+ exit(null);
+ }
+ }
+ };
+ for (SuspendedContext sc : suspended.values()) runRegistersTest(sc, done);
+ }
+ else {
+ exit(null);
+ }
+ }
+
+ private void getTestList() {
+ if (srv_diag == null) {
+ test_list = new String[0];
+ runTest();
+ return;
+ }
+ srv_diag.getTestList(new IDiagnostics.DoneGetTestList() {
+ public void doneGetTestList(IToken token, Throwable error, String[] list) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ if (list == null) list = new String[0];
+ if (list.length > 0) test_id = list[rnd.nextInt(list.length)];
+ test_list = list;
+ runTest();
+ }
+ }
+ });
+ }
+
+ private void setPathMap() {
+ if (srv_path_map == null || path_map == null) {
+ path_map_done = true;
+ runTest();
+ return;
+ }
+ srv_path_map.set(path_map.toArray(new PathMapRule[path_map.size()]), new IPathMap.DoneSet() {
+ public void doneSet(IToken token, Exception error) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ path_map_done = true;
+ runTest();
+ }
+ }
+ });
+ }
+
+ private void setMemMap() {
+ if (mem_map == null || mem_map.size() == 0) {
+ mem_map_done = true;
+ runTest();
+ return;
+ }
+ final Set<IToken> cmds = new HashSet<IToken>();
+ for (String id : mem_map.keySet()) {
+ ArrayList<MemoryRegion> l = mem_map.get(id);
+ cmds.add(srv_memory_map.set(id, l.toArray(new MemoryRegion[l.size()]), new IMemoryMap.DoneSet() {
+ public void doneSet(IToken token, Exception error) {
+ cmds.remove(token);
+ if (error instanceof IErrorReport) {
+ IErrorReport e = (IErrorReport)error;
+ if (e.getErrorCode() == IErrorReport.TCF_ERROR_INV_CONTEXT) error = null;
+ }
+ if (error != null) {
+ exit(error);
+ }
+ else if (cmds.size() == 0) {
+ mem_map_done = true;
+ runTest();
+ }
+ }
+ }));
+ }
+ assert cmds.size() > 0;
+ }
+
+ private void resetBreakpoints() {
+ if (srv_breakpoints == null) {
+ bp_reset_done = true;
+ runTest();
+ return;
+ }
+ // Reset breakpoint list (previous tests might left breakpoints)
+ srv_breakpoints.set(null, new IBreakpoints.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ bp_reset_done = true;
+ runTest();
+ }
+ });
+ }
+
+ private void getBreakpointCapabilities() {
+ if (srv_breakpoints == null) {
+ bp_capabilities = new HashMap<String,Object>();
+ runTest();
+ return;
+ }
+ srv_breakpoints.getCapabilities(test_ctx_id, new IBreakpoints.DoneGetCapabilities() {
+ public void doneGetCapabilities(IToken token, Exception error, Map<String,Object> capabilities) {
+ if (!test_suite.isActive(TestRCBP1.this)) return;
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ Boolean l = (Boolean)capabilities.get(IBreakpoints.CAPABILITY_LOCATION);
+ Boolean c = (Boolean)capabilities.get(IBreakpoints.CAPABILITY_CONDITION);
+ if (l == null || !l) {
+ exit(new Exception("Breakpoints service does not support \"Location\" attribute"));
+ return;
+ }
+ if (c == null || !c) {
+ exit(new Exception("Breakpoints service does not support \"Condition\" attribute"));
+ return;
+ }
+ bp_capabilities = capabilities;
+ runTest();
+ }
+ });
+ }
+
+ private void startTestContext() {
+ srv_diag.runTest(test_id, new IDiagnostics.DoneRunTest() {
+ public void doneRunTest(IToken token, Throwable error, String context_id) {
+ if (error != null) {
+ exit(error);
+ }
+ else if (test_suite.isActive(TestRCBP1.this)) {
+ assert test_ctx_id == null;
+ test_ctx_id = context_id;
+ if (pending_cancel != null) {
+ exit(null);
+ }
+ else {
+ runTest();
+ }
+ }
+ }
+ });
+ }
+
+ private void getTestContext() {
+ srv_run_ctrl.getContext(test_ctx_id, new IRunControl.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, RunControlContext context) {
+ if (test_suite.cancel) return;
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ test_context = context;
+ assert test_ctx_id.equals(context.getID());
+ srv_run_ctrl.addListener(TestRCBP1.this);
+ runTest();
+ }
+ });
+ }
+
+ private void getSymbols() {
+ final HashMap<IToken,String> cmds = new HashMap<IToken,String>();
+ IDiagnostics.DoneGetSymbol done = new IDiagnostics.DoneGetSymbol() {
+ public void doneGetSymbol(IToken token, Throwable error, ISymbol symbol) {
+ String name = cmds.remove(token);
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ if (!test_suite.isActive(TestRCBP1.this)) return;
+ assert test_ctx_id != null;
+ if (symbol == null) {
+ exit(new Exception("Symbol must not be NULL: " + name));
+ }
+ else if (!symbol.isAbs()) {
+ exit(new Exception("Symbol must be absolute: " + name));
+ }
+ else if (symbol.getValue() == null || symbol.getValue().longValue() == 0) {
+ exit(new Exception("Symbol value must not be NULL: " + name));
+ }
+ else {
+ sym_list.put(name, symbol);
+ if (cmds.isEmpty()) runTest();
+ }
+ }
+ };
+ String[] syms = {
+ "tcf_test_func0",
+ "tcf_test_func1",
+ "tcf_test_func2",
+ "tcf_test_func3",
+ "tcf_test_array"
+ };
+ String prs = test_context.getProcessID();
+ for (String name : syms) cmds.put(srv_diag.getSymbol(prs, name, done), name);
+ }
+
+ private void getDataBPFile() {
+ ISymbol sym = sym_list.get("tcf_test_func3");
+ if (sym == null || srv_line_numbers == null) {
+ data_bp_area_done = true;
+ runTest();
+ return;
+ }
+ srv_line_numbers.mapToSource(test_ctx_id, sym.getValue(), sym.getValue().longValue() + 1, new ILineNumbers.DoneMapToSource() {
+ public void doneMapToSource(IToken token, Exception error, CodeArea[] areas) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ if (areas != null && areas.length > 0) data_bp_area = areas[0];
+ data_bp_area_done = true;
+ runTest();
+ }
+ }
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ private void iniBreakpoints() {
+ assert !bp_set_done;
+ assert bp_list.isEmpty();
+ Map<String,Object> m[] = new Map[8];
+ for (int i = 0; i < m.length; i++) {
+ m[i] = new HashMap<String,Object>();
+ m[i].put(IBreakpoints.PROP_ID, "TcfTestBP" + i + "" + channel_id);
+ m[i].put(IBreakpoints.PROP_ENABLED, Boolean.TRUE);
+ switch (i) {
+ case 0:
+ m[i].put(IBreakpoints.PROP_LOCATION, sym_list.get("tcf_test_func0").getValue().toString());
+ // Condition is always true
+ m[i].put(IBreakpoints.PROP_CONDITION, "$thread!=\"\"");
+ break;
+ case 1:
+ m[i].put(IBreakpoints.PROP_LOCATION, sym_list.get("tcf_test_func0").getValue().toString());
+ // Condition is always false
+ m[i].put(IBreakpoints.PROP_CONDITION, "$thread==\"\"");
+ break;
+ case 2:
+ // Second breakpoint at same address
+ m[i].put(IBreakpoints.PROP_LOCATION, "tcf_test_func0");
+ break;
+ case 3:
+ // Location is an expression
+ m[i].put(IBreakpoints.PROP_LOCATION, "(31+1)/16+tcf_test_func1-2");
+ // Condition is always true
+ m[i].put(IBreakpoints.PROP_CONDITION, "tcf_test_func0!=tcf_test_func1");
+ break;
+ case 4:
+ // Disabled breakpoint
+ m[i].put(IBreakpoints.PROP_LOCATION, "tcf_test_func2");
+ m[i].put(IBreakpoints.PROP_ENABLED, Boolean.FALSE);
+ break;
+ case 5:
+ // Breakpoint that will be enabled with "enable" command
+ m[i].put(IBreakpoints.PROP_LOCATION, "tcf_test_func2");
+ m[i].put(IBreakpoints.PROP_ENABLED, Boolean.FALSE);
+ break;
+ case 6:
+ m[i].put(IBreakpoints.PROP_LOCATION, "tcf_test_func3");
+ break;
+ case 7:
+ // Data breakpoint
+ m[i].put(IBreakpoints.PROP_LOCATION, "&tcf_test_char");
+ m[i].put(IBreakpoints.PROP_ACCESSMODE, IBreakpoints.ACCESSMODE_WRITE);
+ Number ca = (Number)bp_capabilities.get(IBreakpoints.CAPABILITY_ACCESSMODE);
+ if (data_bp_area != null && ca != null && (ca.intValue() & IBreakpoints.ACCESSMODE_WRITE) != 0) {
+ m[i].put(IBreakpoints.PROP_FILE, data_bp_area.file);
+ m[i].put(IBreakpoints.PROP_LINE, data_bp_area.start_line);
+ data_bp_id = (String)m[i].get(IBreakpoints.PROP_ID);
+ }
+ else {
+ m[i].put(IBreakpoints.PROP_ENABLED, Boolean.FALSE);
+ }
+ break;
+ }
+ bp_list.put((String)m[i].get(IBreakpoints.PROP_ID), m[i]);
+ }
+ srv_breakpoints.set(m, new IBreakpoints.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ assert !bp_set_done;
+ bp_set_done = true;
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ runTest();
+ }
+ });
+ }
+
+ private void getContextState(final String id) {
+ get_state_cmds.add(srv_run_ctrl.getChildren(id, new IRunControl.DoneGetChildren() {
+ public void doneGetChildren(IToken token, Exception error, String[] contexts) {
+ get_state_cmds.remove(token);
+ if (test_suite.cancel) return;
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ for (String s : contexts) getContextState(s);
+ if (get_state_cmds.isEmpty()) doneContextState();
+ }
+ }));
+ if (id == null) return;
+ get_state_cmds.add(srv_run_ctrl.getContext(id, new IRunControl.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, RunControlContext ctx) {
+ get_state_cmds.remove(token);
+ if (test_suite.cancel) return;
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ if (test_id != null) {
+ assert test_ctx_id != null;
+ assert isMyContext(ctx);
+ for (ITCFTest t : test_suite.getActiveTests()) {
+ if (t != TestRCBP1.this && t instanceof TestRCBP1 && ((TestRCBP1)t).threads.get(id) != null) {
+ exit(new Exception("Invalid or missing 'CreatorID' context attribute.\nContext: " + ctx));
+ return;
+ }
+ }
+ }
+ if (ctx.hasState()) {
+ threads.put(id, ctx);
+ get_state_cmds.add(ctx.getState(new IRunControl.DoneGetState() {
+ public void doneGetState(IToken token, Exception error,
+ boolean susp, String pc, String reason,
+ Map<String, Object> params) {
+ get_state_cmds.remove(token);
+ if (test_suite.cancel) return;
+ if (error != null) {
+ exit(new Exception("Cannot get context state", error));
+ return;
+ }
+ if (!susp) {
+ if (suspended.get(id) != null) {
+ exit(new Exception("Invalid result of getState command"));
+ return;
+ }
+ running.add(id);
+ }
+ else {
+ assert threads.get(id) != null;
+ if (running.contains(id)) {
+ exit(new Exception("Invalid result of getState command"));
+ return;
+ }
+ SuspendedContext sc = suspended.get(id);
+ if (sc != null && sc.pc != null && !sc.pc.equals(pc)) {
+ exit(new Exception("Invalid result of getState command: invalid PC. Context: " + id));
+ return;
+ }
+ if (sc != null && sc.reason != null && !sc.reason.equals(reason)) {
+ exit(new Exception("Invalid result of getState command: invalid suspend reason. Context: " + id));
+ return;
+ }
+ if (test_id != null && "Breakpoint".equals(reason)) {
+ exit(new Exception("Invalid suspend reason of main thread " +
+ id + " after test start: " + reason + " " + pc));
+ return;
+ }
+ assert !done_get_state;
+ suspended.put(id, new SuspendedContext(id, pc, reason, params));
+ }
+ if (get_state_cmds.isEmpty()) doneContextState();
+ }
+ }));
+ }
+ if (get_state_cmds.isEmpty()) doneContextState();
+ }
+ }));
+ }
+
+ private void doneContextState() {
+ assert !done_get_state;
+ assert get_state_cmds.isEmpty();
+ assert resume_cnt == 0;
+ assert threads.size() == suspended.size() + running.size();
+ done_get_state = true;
+ runTest();
+ }
+
+ private void getDisassemlyLines() {
+ for (final String id : suspended.keySet()) {
+ SuspendedContext sc = suspended.get(id);
+ get_state_cmds.add(srv_disassembly.getCapabilities(id, new IDisassembly.DoneGetCapabilities() {
+ public void doneGetCapabilities(IToken token, Throwable error, Map<String,Object>[] arr) {
+ get_state_cmds.remove(token);
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ disassembly_capabilities.put(id, arr);
+ if (get_state_cmds.isEmpty()) doneDisassembly();
+ }
+ }
+ }));
+ if (sc.pc == null) {
+ disassembly_lines.put(id, new IDisassemblyLine[0]);
+ continue;
+ }
+ BigInteger pc = new BigInteger(sc.pc);
+ get_state_cmds.add(srv_disassembly.disassemble(id, pc, 1, null, new IDisassembly.DoneDisassemble() {
+ public void doneDisassemble(IToken token, Throwable error, IDisassemblyLine[] arr) {
+ get_state_cmds.remove(token);
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ disassembly_lines.put(id, arr);
+ if (get_state_cmds.isEmpty()) doneDisassembly();
+ }
+ }
+ }));
+ }
+ if (get_state_cmds.isEmpty()) doneDisassembly();
+ }
+
+ private void doneDisassembly() {
+ assert !done_disassembly;
+ assert get_state_cmds.isEmpty();
+ if (!test_suite.isActive(TestRCBP1.this)) return;
+ assert suspended.size() == disassembly_lines.size();
+ done_disassembly = true;
+ runTest();
+ }
+
+ private void changeBreakpoints() {
+ assert !bp_change_done;
+ final String bp_id = "TcfTestBP5" + channel_id;
+ final Map<String,Object> m = bp_list.get(bp_id);
+ ArrayList<String> l = new ArrayList<String>();
+ l.add(test_context.getProcessID());
+ Boolean ci = (Boolean)bp_capabilities.get(IBreakpoints.CAPABILITY_CONTEXTIDS);
+ if (ci != null && ci) m.put(IBreakpoints.PROP_CONTEXTIDS, l);
+ Boolean sg = (Boolean)bp_capabilities.get(IBreakpoints.CAPABILITY_STOP_GROUP);
+ if (sg != null && sg) m.put(IBreakpoints.PROP_STOP_GROUP, l);
+ StringBuffer bf = new StringBuffer();
+ for (String id : threads.keySet()) {
+ if (bf.length() > 0) bf.append(" || ");
+ bf.append("$thread==\"");
+ bf.append(id);
+ bf.append('"');
+ }
+ m.put(IBreakpoints.PROP_CONDITION, bf.toString());
+ bp_list.put(bp_id, m);
+ srv_breakpoints.change(m, new IBreakpoints.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ bp_change_done = true;
+ if (error != null) exit(error);
+ }
+ });
+ srv_breakpoints.getIDs(new IBreakpoints.DoneGetIDs() {
+ public void doneGetIDs(IToken token, Exception error, String[] ids) {
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ if (!bp_change_done) {
+ exit(new Exception("Invalid responce order"));
+ return;
+ }
+ HashSet<String> s = new HashSet<String>();
+ for (String id : ids) s.add(id);
+ if (ids.length != s.size()) {
+ exit(new Exception("Invalis BP list: " + ids));
+ return;
+ }
+ for (String id : bp_list.keySet()) {
+ if (!s.contains(id)) {
+ exit(new Exception("BP is not listed by Breakpoints.getIDs: " + id));
+ return;
+ }
+ }
+ }
+ });
+ for (final String id : bp_list.keySet()) {
+ srv_breakpoints.getProperties(id, new IBreakpoints.DoneGetProperties() {
+ public void doneGetProperties(IToken token, Exception error, Map<String,Object> properties) {
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ HashMap<String,Object> m0 = new HashMap<String,Object>(properties);
+ HashMap<String,Object> m1 = (HashMap<String,Object>)bp_list.get(id);
+ if (m0.get(IBreakpoints.PROP_ENABLED) == null) m0.put(IBreakpoints.PROP_ENABLED, Boolean.FALSE);
+ if (m1.get(IBreakpoints.PROP_ENABLED) == null) m1.put(IBreakpoints.PROP_ENABLED, Boolean.FALSE);
+ if (!m1.equals(m0)) {
+ exit(new Exception("Invalid data returned by Breakpoints.getProperties: " + m0 + " != " + m1));
+ return;
+ }
+ }
+ });
+ srv_breakpoints.getStatus(id, new IBreakpoints.DoneGetStatus() {
+ public void doneGetStatus(IToken token, Exception error, Map<String,Object> status) {
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ }
+ });
+ }
+ Protocol.sync(new Runnable() {
+ public void run() {
+ if (!test_suite.isActive(TestRCBP1.this)) return;
+ if (!bp_change_done) {
+ exit(new Exception("Protocol.sync() test failed"));
+ return;
+ }
+ m.put(IBreakpoints.PROP_ENABLED, Boolean.TRUE);
+ srv_breakpoints.enable(new String[]{ bp_id }, new IBreakpoints.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ if (error != null) exit(error);
+ }
+ });
+ bp_sync_done = true;
+ runTest();
+ }
+ });
+ }
+
+ public void containerResumed(String[] context_ids) {
+ for (String id : context_ids) contextResumed(id);
+ }
+
+ 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)) continue;
+ contextSuspended(id, null, null, null);
+ }
+ contextSuspended(context, pc, reason, params);
+ }
+
+ public void contextAdded(RunControlContext[] contexts) {
+ for (RunControlContext ctx : contexts) {
+ String id = ctx.getID();
+ if (threads.get(id) != null) {
+ exit(new Exception("Invalid contextAdded event:\nContext: " + ctx));
+ return;
+ }
+ if (isMyContext(ctx)) {
+ for (ITCFTest t : test_suite.getActiveTests()) {
+ if (t instanceof TestRCBP1 && ((TestRCBP1)t).threads.get(id) != null) {
+ exit(new Exception("Invalid or missing 'CreatorID' context attribute.\nContext: " + ctx));
+ return;
+ }
+ }
+ if (threads.size() > 0 && !all_setup_done) {
+ assert !canResume(id);
+ exit(new Exception("Unexpected contextAdded event\nContext: " + ctx));
+ return;
+ }
+ if (ctx.hasState()) {
+ threads.put(id, ctx);
+ if (!done_get_state) {
+ getContextState(id);
+ }
+ else {
+ running.add(id);
+ }
+ }
+ }
+ }
+ }
+
+ public void contextChanged(RunControlContext[] contexts) {
+ for (RunControlContext ctx : contexts) {
+ String id = ctx.getID();
+ if (id.equals(test_ctx_id)) test_context = ctx;
+ if (threads.get(id) != null) {
+ assert isMyContext(ctx);
+ threads.put(id, ctx);
+ }
+ }
+ }
+
+ public void contextException(String id, String msg) {
+ RunControlContext ctx = threads.get(id);
+ if (ctx != null) {
+ assert isMyContext(ctx);
+ exit(new Exception("Context exception: " + msg));
+ }
+ }
+
+ public void contextRemoved(String[] contexts) {
+ for (String id : contexts) {
+ if (suspended.get(id) != null) {
+ exit(new Exception("Invalid contextRemoved event"));
+ return;
+ }
+ running.remove(id);
+ if (threads.remove(id) != null && threads.isEmpty()) {
+ if (bp_cnt != 40) {
+ exit(new Exception("Test main thread breakpoint count = " + bp_cnt + ", expected 40"));
+ }
+ if (data_bp_id != null && data_bp_cnt != 10) {
+ exit(new Exception("Test main thread data breakpoint count = " + data_bp_cnt + ", expected 10"));
+ }
+ srv_run_ctrl.removeListener(this);
+ // Reset breakpoint list
+ bp_list.clear();
+ srv_breakpoints.set(null, new IBreakpoints.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ exit(error);
+ }
+ });
+ }
+ }
+ }
+
+ public void contextResumed(String id) {
+ IRunControl.RunControlContext ctx = threads.get(id);
+ if (ctx == null) return;
+ assert isMyContext(ctx);
+ if (!ctx.hasState()) {
+ exit(new Exception("Resumed event for context that HasState = false"));
+ return;
+ }
+ SuspendedContext sc = suspended.remove(id);
+ if (!done_get_state || sc == null || !sc.ok_to_resume || sc.get_state_pending && ctx.getRCGroup() == null) {
+ assert !canResume(id);
+ exit(new Exception("Unexpected contextResumed event: " + id));
+ return;
+ }
+ if (isMyBreakpoint(sc)) suspended_prev.put(id, sc);
+ running.add(id);
+ }
+
+ private long getSymAddr(String sym) {
+ return sym_list.get(sym).getValue().longValue();
+ }
+
+ private String toSymName(long addr) {
+ for (String name : sym_list.keySet()) {
+ if (getSymAddr(name) == addr) return name;
+ }
+ return "0x" + Long.toHexString(addr);
+ }
+
+ private void checkSuspendedContext(SuspendedContext sc, String sym) {
+ long pc = Long.parseLong(sc.pc);
+ long ss = getSymAddr(sym);
+ if (pc != ss || !"Breakpoint".equals(sc.reason)) {
+ exit(new Exception("Invalid contextSuspended event: " +
+ sc.id + " '" + toSymName(pc) + "' " + sc.pc + " " + sc.reason +
+ ", expected breakpoint at '" + sym + "' " + ss));
+ }
+ String bp_id = null;
+ if (sc.params != null) {
+ Object ids = sc.params.get(IRunControl.STATE_BREAKPOINT_IDS);
+ if (ids != null) {
+ @SuppressWarnings("unchecked")
+ Collection<String> c = (Collection<String>)ids;
+ HashSet<String> set = new HashSet<String>();
+ for (String id : c) {
+ if (!set.add(id)) {
+ exit(new Exception("Invalid value of 'BPs' attribute: duplicate items"));
+ return;
+ }
+ if (bp_list.get(id) != null) {
+ bp_id = id;
+ break;
+ }
+ }
+ if (bp_id == null) {
+ exit(new Exception("Invalid value of 'BPs' attribute in a context state"));
+ }
+ }
+ }
+ }
+
+ private void checkSuspendedContext(final SuspendedContext sc) {
+ boolean my_breakpoint = isMyBreakpoint(sc);
+ if (main_thread_id == null && my_breakpoint) {
+ // Process main thread should be the first to hit a breakpoint in the test
+ if (!done_get_state) {
+ exit(new Exception("Unexpeceted breakpoint hit"));
+ return;
+ }
+ main_thread_id = sc.id;
+ }
+ if (main_thread_id == null) {
+ if (all_setup_done) resume(sc.id);
+ return;
+ }
+ if (my_breakpoint) {
+ if (sc.id.equals(main_thread_id)) bp_cnt++;
+ SuspendedContext sp = suspended_prev.get(sc.id);
+ String sp_sym = sp == null ? null : toSymName(Long.parseLong(sp.pc));
+ if (sp == null) {
+ checkSuspendedContext(sc, "tcf_test_func0");
+ }
+ else if ("tcf_test_func0".equals(sp_sym)) {
+ checkSuspendedContext(sc, "tcf_test_func1");
+ }
+ else if ("tcf_test_func1".equals(sp_sym)) {
+ if (sc.id.equals(main_thread_id)) {
+ checkSuspendedContext(sc, "tcf_test_func2");
+ }
+ else {
+ checkSuspendedContext(sc, "tcf_test_func3");
+ }
+ }
+ else if ("tcf_test_func2".equals(sp_sym)) {
+ checkSuspendedContext(sc, "tcf_test_func3");
+ }
+ else if ("tcf_test_func3".equals(sp_sym)) {
+ checkSuspendedContext(sc, "tcf_test_func0");
+ }
+ }
+ else if (isMyDataBreakpoint(sc)) {
+ if (sc.id.equals(main_thread_id)) data_bp_cnt++;
+ }
+ if (!all_setup_done) return;
+ if (!test_suite.isActive(this)) return;
+ Runnable done = new Runnable() {
+ public void run() {
+ if (suspended.get(sc.id) == sc) resume(sc.id);
+ }
+ };
+ if (my_breakpoint) {
+ switch (rnd.nextInt(5)) {
+ case 0:
+ runMemoryTest(sc, done);
+ break;
+ case 1:
+ runRegistersTest(sc, done);
+ break;
+ case 2:
+ runLineNumbersTest(sc, done);
+ break;
+ case 3:
+ runSymbolsTest(sc, done);
+ break;
+ default:
+ done.run();
+ break;
+ }
+ }
+ else {
+ done.run();
+ }
+ }
+
+ private boolean isMyContext(IRunControl.RunControlContext ctx) {
+ // Check if the context was created by this test
+ if (test_ctx_id == null) return false;
+ return test_ctx_id.equals(ctx.getID()) ||
+ test_ctx_id.equals(ctx.getParentID()) ||
+ test_ctx_id.equals(ctx.getCreatorID());
+ }
+
+ private boolean isMyBreakpoint(SuspendedContext sc) {
+ // Check if the context is suspended by one of our breakpoints
+ if (!"Breakpoint".equals(sc.reason)) return false;
+ long pc = Long.parseLong(sc.pc);
+ for (IDiagnostics.ISymbol sym : sym_list.values()) {
+ if (pc == sym.getValue().longValue()) return true;
+ }
+ return false;
+ }
+
+ private boolean isMyDataBreakpoint(SuspendedContext sc) {
+ // Check if the context is suspended by our data breakpoints
+ if (data_bp_id == null) return false;
+ if (!"Breakpoint".equals(sc.reason)) return false;
+ if (sc.params == null) return false;
+ Object ids = sc.params.get(IRunControl.STATE_BREAKPOINT_IDS);
+ if (ids != null) {
+ @SuppressWarnings("unchecked")
+ Collection<String> c = (Collection<String>)ids;
+ if (c.contains(data_bp_id)) return true;
+ }
+ return false;
+ }
+
+ public void contextSuspended(final String id, String pc, String reason, Map<String, Object> params) {
+ IRunControl.RunControlContext ctx = threads.get(id);
+ if (ctx == null) return;
+ assert isMyContext(ctx);
+ if (!ctx.hasState()) {
+ exit(new Exception("Suspended event for context that HasState = false"));
+ return;
+ }
+ running.remove(id);
+ SuspendedContext sc = suspended.get(id);
+ if (sc != null) {
+ if (done_get_state || pc != null && !sc.pc.equals(pc) || reason != null && !sc.reason.equals(reason)) {
+ exit(new Exception("Invalid contextSuspended event"));
+ return;
+ }
+ }
+ else {
+ sc = new SuspendedContext(id, pc, reason, params);
+ assert !done_get_state || done_disassembly || srv_disassembly == null;
+ suspended.put(id, sc);
+ }
+ if (!all_setup_done) return;
+ assert get_state_cmds.size() == 0;
+ assert suspended.get(id) == sc;
+ assert !sc.get_state_pending;
+ sc.get_state_pending = true;
+ final SuspendedContext sc0 = sc;
+ ctx.getState(new IRunControl.DoneGetState() {
+ public void doneGetState(IToken token, Exception error, boolean susp,
+ String pc, String reason, Map<String, Object> params) {
+ if (error != null) {
+ exit(new Exception("Cannot get context state", error));
+ }
+ else if (suspended.get(id) != sc0) {
+ exit(new Exception("Context resumed before RunControl.getState result"));
+ }
+ else if (!susp) {
+ exit(new Exception("Invalid RunControl.getState result"));
+ }
+ else if (pc == null || pc.equals("0")) {
+ exit(new Exception("Invalid PC returned by RunControl.getState"));
+ }
+ else if (test_suite.isActive(TestRCBP1.this)) {
+ SuspendedContext sc = suspended.get(id);
+ assert sc.get_state_pending;
+ sc.get_state_pending = false;
+ if (sc.pc == null || sc.reason == null) {
+ sc = new SuspendedContext(id, pc, reason, params);
+ assert !done_get_state || done_disassembly || srv_disassembly == null;
+ suspended.put(id, sc);
+ }
+ else if (!sc.pc.equals(pc) || !sc.reason.equals(reason)) {
+ exit(new Exception("Invalid RunControl.getState result"));
+ return;
+ }
+ checkSuspendedContext(sc);
+ }
+ }
+ });
+ }
+
+ public boolean canResume(String id) {
+ if (test_ctx_id != null && threads.size() == 0)
+ // Don't know yet neither my thread IDs nor my RC group.
+ return false;
+ IRunControl.RunControlContext ctx = test_rc.getContext(id);
+ if (ctx == null) return false;
+ if (isMyContext(ctx) && (!all_setup_done || threads.get(id) == null))
+ // My threads should stay suspended until all_setup_done
+ return false;
+ String grp = ctx.getRCGroup();
+ for (IRunControl.RunControlContext x : threads.values()) {
+ if (x.getID().equals(id) || grp != null && grp.equals(x.getRCGroup())) {
+ SuspendedContext sc = suspended.get(x.getID());
+ if (sc == null) return false;
+ if (!sc.ok_to_resume) return false;
+ }
+ }
+ return true;
+ }
+
+ private void resume(String id) {
+ assert done_get_state || resume_cnt == 0;
+ assert bp_sync_done;
+ assert mem_map_test_done;
+ resume_cnt++;
+ SuspendedContext sc = suspended.get(id);
+ IRunControl.RunControlContext ctx = threads.get(id);
+ if (ctx != null && sc != null) {
+ assert !sc.get_state_pending;
+ assert !sc.ok_to_resume;
+ sc.ok_to_resume = true;
+ int rm = IRunControl.RM_RESUME;
+ if (isMyBreakpoint(sc)) {
+ rm = rnd.nextInt(6);
+ if (!ctx.canResume(rm)) rm = IRunControl.RM_RESUME;
+ }
+ test_rc.resume(id, rm);
+ }
+ }
+
+ private void runMemoryTest(final SuspendedContext sc, final Runnable done) {
+ if (srv_memory == null || test_suite.target_lock) {
+ Protocol.invokeLater(done);
+ return;
+ }
+ test_suite.target_lock = true;
+ srv_memory.getContext(test_context.getProcessID(), new IMemory.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, final MemoryContext mem_ctx) {
+ if (suspended.get(sc.id) != sc) {
+ test_suite.target_lock = false;
+ return;
+ }
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ if (!test_context.getProcessID().equals(mem_ctx.getID())) {
+ exit(new Exception("Bad memory context data: invalid ID"));
+ }
+ final boolean big_endian = mem_ctx.isBigEndian();
+ final int addr_size = mem_ctx.getAddressSize();
+ final byte[] buf = new byte[0x1000];
+ mem_ctx.get(sym_list.get("tcf_test_array").getValue(), 1, buf, 0, addr_size, 0, new IMemory.DoneMemory() {
+ public void doneMemory(IToken token, MemoryError error) {
+ if (suspended.get(sc.id) != sc) {
+ test_suite.target_lock = false;
+ return;
+ }
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ byte[] tmp = new byte[addr_size + 1];
+ tmp[0] = 0; // Extra byte to avoid sign extension by BigInteger
+ if (big_endian) {
+ System.arraycopy(buf, 0, tmp, 1, addr_size);
+ }
+ else {
+ for (int i = 0; i < addr_size; i++) {
+ tmp[i + 1] = buf[addr_size - i - 1];
+ }
+ }
+ Number mem_address = new BigInteger(tmp);
+ if (mem_address.longValue() == 0) {
+ exit(new Exception("Bad value of 'tcf_test_array': " + mem_address));
+ }
+ testSetMemoryCommand(sc, mem_ctx, mem_address, buf, done);
+ }
+ });
+ }
+ });
+ }
+
+ private void testSetMemoryCommand(final SuspendedContext sc,
+ final IMemory.MemoryContext mem_ctx,
+ final Number addr, final byte[] buf,
+ final Runnable done) {
+ final byte[] data = new byte[buf.length];
+ rnd.nextBytes(data);
+ mem_ctx.set(addr, 1, data, 0, data.length, 0, new IMemory.DoneMemory() {
+ public void doneMemory(IToken token, MemoryError error) {
+ if (suspended.get(sc.id) != sc) {
+ test_suite.target_lock = false;
+ return;
+ }
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ mem_ctx.get(addr, 1, buf, 0, buf.length, 0, new IMemory.DoneMemory() {
+ public void doneMemory(IToken token, MemoryError error) {
+ if (suspended.get(sc.id) != sc) {
+ test_suite.target_lock = false;
+ return;
+ }
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ for (int i = 0; i < data.length; i++) {
+ if (data[i] != buf[i]) {
+ exit(new Exception(
+ "Invalid Memory.get responce: wrong data at offset " + i +
+ ", expected " + data[i] + ", actual " + buf[i]));
+ return;
+ }
+ }
+ testFillMemoryCommand(sc, mem_ctx, addr, buf, done);
+ }
+ });
+ }
+ });
+ }
+
+ private void testFillMemoryCommand(final SuspendedContext sc,
+ final IMemory.MemoryContext mem_ctx,
+ final Number addr, final byte[] buf,
+ final Runnable done) {
+ final byte[] data = new byte[buf.length / 7];
+ rnd.nextBytes(data);
+ mem_ctx.fill(addr, 1, data, buf.length, 0, new IMemory.DoneMemory() {
+ public void doneMemory(IToken token, MemoryError error) {
+ if (suspended.get(sc.id) != sc) {
+ test_suite.target_lock = false;
+ return;
+ }
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ mem_ctx.get(addr, 1, buf, 0, buf.length, 0, new IMemory.DoneMemory() {
+ public void doneMemory(IToken token, MemoryError error) {
+ if (suspended.get(sc.id) != sc) {
+ test_suite.target_lock = false;
+ return;
+ }
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ for (int i = 0; i < data.length; i++) {
+ if (data[i % data.length] != buf[i]) {
+ exit(new Exception(
+ "Invalid Memory.get responce: wrong data at offset " + i +
+ ", expected " + data[i % data.length] + ", actual " + buf[i]));
+ return;
+ }
+ }
+ test_suite.target_lock = false;
+ done.run();
+ }
+ });
+ }
+ });
+ }
+
+ private void runRegistersTest(final SuspendedContext sc, final Runnable done) {
+ if (srv_registers == null) {
+ Protocol.invokeLater(done);
+ return;
+ }
+ if (regs.get(sc.id) == null) {
+ final Map<String,IRegisters.RegistersContext> reg_map =
+ new HashMap<String,IRegisters.RegistersContext>();
+ regs.put(sc.id, reg_map);
+ final Set<IToken> cmds = new HashSet<IToken>();
+ cmds.add(srv_registers.getChildren(sc.id, new IRegisters.DoneGetChildren() {
+ public void doneGetChildren(IToken token, Exception error, String[] context_ids) {
+ cmds.remove(token);
+ if (suspended.get(sc.id) != sc) {
+ regs.remove(sc.id);
+ return;
+ }
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ for (final String id : context_ids) {
+ cmds.add(srv_registers.getChildren(id, this));
+ cmds.add(srv_registers.getContext(id, new IRegisters.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, RegistersContext context) {
+ cmds.remove(token);
+ if (suspended.get(sc.id) != sc) {
+ regs.remove(sc.id);
+ return;
+ }
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ reg_map.put(id, context);
+ if (cmds.isEmpty()) {
+ testGetSetRegisterCommands(sc, done);
+ }
+ }
+ }));
+ }
+ }
+ }));
+ }
+ else {
+ testGetSetRegisterCommands(sc, done);
+ }
+ }
+
+ private void testGetSetRegisterCommands(final SuspendedContext sc, final Runnable done) {
+ Map<String,IRegisters.RegistersContext> reg_map = regs.get(sc.id);
+ final Set<IToken> cmds = new HashSet<IToken>();
+ for (final IRegisters.RegistersContext ctx : reg_map.values()) {
+ if (!ctx.isReadable()) continue;
+ if (ctx.isReadOnce()) continue;
+ cmds.add(ctx.get(new IRegisters.DoneGet() {
+ public void doneGet(IToken token, Exception error, final byte[] value) {
+ cmds.remove(token);
+ if (suspended.get(sc.id) != sc) return;
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ if (ctx.getSize() != value.length) {
+ exit(new Exception("Invalid register value size"));
+ return;
+ }
+ if (ctx.isWriteable() && !ctx.isWriteOnce()) {
+ cmds.add(ctx.set(value, new IRegisters.DoneSet() {
+ public void doneSet(IToken token, Exception error) {
+ cmds.remove(token);
+ if (suspended.get(sc.id) != sc) return;
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ cmds.add(ctx.get(new IRegisters.DoneGet() {
+ public void doneGet(IToken token, Exception error, byte[] value1) {
+ cmds.remove(token);
+ if (suspended.get(sc.id) != sc) return;
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ for (int i = 0; i < value.length; i++) {
+ if (value[i] != value1[i]) {
+ exit(new Exception("Invalid register value"));
+ return;
+ }
+ }
+ if (cmds.isEmpty()) {
+ done.run();
+ }
+ }
+ }));
+ }
+ }));
+ }
+ if (cmds.isEmpty()) {
+ done.run();
+ }
+ }
+ }));
+ }
+ if (!reg_map.isEmpty()) {
+ int data_size = 0;
+ List<IRegisters.Location> locs = new ArrayList<IRegisters.Location>();
+ String[] ids = reg_map.keySet().toArray(new String[reg_map.size()]);
+ for (int i = 0; i < rnd.nextInt(32); i++) {
+ String id = ids[rnd.nextInt(ids.length)];
+ IRegisters.RegistersContext ctx = reg_map.get(id);
+ if (!ctx.isReadable()) continue;
+ if (!ctx.isWriteable()) continue;
+ if (ctx.isReadOnce()) continue;
+ if (ctx.isWriteOnce()) continue;
+ if (ctx.getSize() == 0) continue;
+ int offs = rnd.nextInt(ctx.getSize());
+ int size = rnd.nextInt(ctx.getSize() - offs) + 1;
+ locs.add(new IRegisters.Location(id, offs, size));
+ data_size += size;
+ }
+ final int total_size = data_size;
+ final IRegisters.Location[] loc_arr = locs.toArray(new IRegisters.Location[locs.size()]);
+ cmds.add(srv_registers.getm(loc_arr, new IRegisters.DoneGet() {
+ public void doneGet(IToken token, Exception error, byte[] value) {
+ cmds.remove(token);
+ if (suspended.get(sc.id) != sc) return;
+ if (error == null && value.length != total_size) {
+ error = new Exception("Invalid data size in Registers.getm reply");
+ }
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ cmds.add(srv_registers.setm(loc_arr, value, new IRegisters.DoneSet() {
+ public void doneSet(IToken token, Exception error) {
+ cmds.remove(token);
+ if (suspended.get(sc.id) != sc) return;
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ if (cmds.isEmpty()) {
+ done.run();
+ }
+ }
+ }));
+ }
+ }));
+ }
+ if (cmds.isEmpty()) {
+ done.run();
+ }
+ }
+
+ private void runLineNumbersTest(SuspendedContext sc, final Runnable done) {
+ if (srv_line_numbers != null && sc.pc != null) {
+ BigInteger x = new BigInteger(sc.pc);
+ BigInteger y = x.add(BigInteger.valueOf(1));
+ srv_line_numbers.mapToSource(sc.id, x, y, new ILineNumbers.DoneMapToSource() {
+ public void doneMapToSource(IToken token, Exception error, CodeArea[] areas) {
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ done.run();
+ }
+ });
+ }
+ else {
+ done.run();
+ }
+ }
+
+ private void runSymbolsTest(final SuspendedContext sc, final Runnable done) {
+ if (srv_syms != null && sc.pc != null) {
+ final BigInteger x = new BigInteger(sc.pc);
+ srv_syms.findByAddr(sc.id, x, new ISymbols.DoneFind() {
+ public void doneFind(IToken token, Exception error, String symbol_id) {
+ if (error != null) {
+ int code = IErrorReport.TCF_ERROR_OTHER;
+ if (error instanceof IErrorReport) code = ((IErrorReport)error).getErrorCode();
+ switch (code) {
+ case IErrorReport.TCF_ERROR_INV_COMMAND:
+ case IErrorReport.TCF_ERROR_SYM_NOT_FOUND:
+ done.run();
+ return;
+ default:
+ exit(error);
+ return;
+ }
+ }
+ srv_syms.getContext(symbol_id, new ISymbols.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, Symbol context) {
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ Number addr = context.getAddress();
+ int size = context.getSize();
+ if (addr == null) {
+ exit(new Exception("Missing symbol address attribute"));
+ return;
+ }
+ if (size <= 0) {
+ exit(new Exception("Invalid symbol size attribute"));
+ return;
+ }
+ BigInteger y = JSON.toBigInteger(addr);
+ BigInteger z = y.add(BigInteger.valueOf(size));
+ if (x.compareTo(y) < 0 || x.compareTo(z) >= 0) {
+ exit(new Exception("Invalid symbol address attribute"));
+ return;
+ }
+ String name = context.getName();
+ if (name == null) {
+ done.run();
+ return;
+ }
+ srv_syms.find(sc.id, 0, name, new ISymbols.DoneFind() {
+ public void doneFind(IToken token, Exception error, String symbol_id) {
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ done.run();
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+ else {
+ done.run();
+ }
+ }
+
+ private String getRandomString() {
+ int l = rnd.nextInt(512) + 1;
+ StringBuffer bf = new StringBuffer(l);
+ for (int i = 0; i < l; i++) {
+ bf.append((char)(rnd.nextInt(0xffff) + 1));
+ }
+ return bf.toString();
+ }
+
+ private void runMemoryMapTest() {
+ assert !mem_map_test_running;
+ if (srv_memory_map == null || test_context == null || test_context.getProcessID() == null) {
+ mem_map_test_done = true;
+ runTest();
+ return;
+ }
+ mem_map_test_running = true;
+ final String prs_id = test_context.getProcessID();
+ srv_memory_map.get(prs_id, new IMemoryMap.DoneGet() {
+ public void doneGet(IToken token, Exception error, MemoryRegion[] map) {
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ final Map<String,Object> props = new HashMap<String,Object>();
+ final String test_id = "TestRCBP1." + mem_map_region_id++;
+ props.put(IMemoryMap.PROP_ID, test_id);
+ if (rnd.nextBoolean()) props.put(IMemoryMap.PROP_ADDRESS, rnd.nextInt(0x10000000));
+ if (rnd.nextBoolean()) props.put(IMemoryMap.PROP_SIZE, rnd.nextInt(0x10000000));
+ if (rnd.nextBoolean()) props.put(IMemoryMap.PROP_FLAGS, rnd.nextInt(0x7));
+ if (rnd.nextBoolean()) {
+ props.put(IMemoryMap.PROP_FILE_NAME, getRandomString());
+ if (rnd.nextBoolean()) props.put(IMemoryMap.PROP_SECTION_NAME, getRandomString());
+ else if (rnd.nextBoolean()) props.put(IMemoryMap.PROP_OFFSET, rnd.nextInt(0x10000000));
+ if (rnd.nextBoolean()) props.put(IMemoryMap.PROP_BSS, true);
+ }
+ List<MemoryRegion> list = new ArrayList<MemoryRegion>();
+ for (MemoryRegion r : map) {
+ String id = (String)r.getProperties().get(IMemoryMap.PROP_ID);
+ if (id != null) list.add(r);
+ }
+ final List<MemoryRegion> org_list = new ArrayList<MemoryRegion>(list);
+ list.add(new MemRegion(props));
+ srv_memory_map.set(prs_id, list.toArray(new MemoryRegion[list.size()]), new IMemoryMap.DoneSet() {
+ public void doneSet(IToken token, Exception error) {
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ srv_memory_map.get(prs_id, new IMemoryMap.DoneGet() {
+ public void doneGet(IToken token, Exception error, MemoryRegion[] map) {
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ int cnt = 0;
+ for (MemoryRegion r : map) {
+ String id = (String)r.getProperties().get(IMemoryMap.PROP_ID);
+ if (!test_id.equals(id)) continue;
+ for (String p : props.keySet()) {
+ if (!props.get(p).equals(r.getProperties().get(p))) {
+ exit(new Error("Invalid value returned for Memory Map region property " + p));
+ return;
+ }
+ }
+ cnt++;
+ }
+ if (cnt != 1) {
+ exit(new Error("Error adding memory map entry with MemoryMap.set command"));
+ return;
+ }
+ srv_memory_map.set(prs_id, org_list.toArray(new MemoryRegion[org_list.size()]), new IMemoryMap.DoneSet() {
+ public void doneSet(IToken token, Exception error) {
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ mem_map_test_running = false;
+ mem_map_test_done = true;
+ runTest();
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+
+ void cancel(final Runnable done) {
+ if (srv_run_ctrl != null) srv_run_ctrl.removeListener(this);
+ if (test_ctx_id == null) {
+ if (pending_cancel != null) {
+ exit(null);
+ }
+ else {
+ pending_cancel = done;
+ }
+ }
+ else if (cancel_test_cmd == null) {
+ cancel_test_cmd = srv_diag.cancelTest(test_ctx_id, new IDiagnostics.DoneCancelTest() {
+ public void doneCancelTest(IToken token, Throwable error) {
+ cancel_test_cmd = null;
+ exit(error);
+ done.run();
+ }
+ });
+ }
+ else {
+ exit(new Exception("Cannot terminate remote test process"));
+ done.run();
+ }
+ }
+
+ private void exit(Throwable x) {
+ if (!test_suite.isActive(this)) return;
+ if (pending_cancel != null) {
+ Protocol.invokeLater(pending_cancel);
+ pending_cancel = null;
+ }
+ else {
+ if (srv_run_ctrl != null) srv_run_ctrl.removeListener(this);
+ }
+ if (srv_breakpoints != null) srv_breakpoints.removeListener(bp_listener);
+ test_suite.done(this, x);
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestStreams.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestStreams.java
new file mode 100644
index 000000000..e066a4b47
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestStreams.java
@@ -0,0 +1,305 @@
+/*******************************************************************************
+ * Copyright (c) 2010, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tm.internal.tcf.debug.tests;
+
+import java.util.HashSet;
+import java.util.Random;
+
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.services.IDiagnostics;
+import org.eclipse.tm.tcf.services.IStreams;
+
+class TestStreams implements ITCFTest, IStreams.StreamsListener {
+
+ private final TCFTestSuite test_suite;
+ private final IDiagnostics diag;
+ private final IStreams streams;
+ private final Random rnd = new Random();
+ private final HashSet<String> stream_ids = new HashSet<String>();
+
+ private String inp_id;
+ private String out_id;
+
+ private int test_count;
+ private long start_time;
+
+ TestStreams(TCFTestSuite test_suite, IChannel channel) {
+ this.test_suite = test_suite;
+ diag = channel.getRemoteService(IDiagnostics.class);
+ streams = channel.getRemoteService(IStreams.class);
+ }
+
+ public void start() {
+ if (diag == null ||streams == null) {
+ test_suite.done(this, null);
+ }
+ else {
+ start_time = System.currentTimeMillis();
+ connect();
+ }
+ }
+
+ private void connect() {
+ diag.createTestStreams(1001, 771, new IDiagnostics.DoneCreateTestStreams() {
+ public void doneCreateTestStreams(IToken token, Throwable error, final String inp_id, final String out_id) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ TestStreams.this.inp_id = inp_id;
+ TestStreams.this.out_id = out_id;
+ if (stream_ids.size() != 0) {
+ exit(new Exception("Stream events without subscription"));
+ return;
+ }
+ streams.connect(inp_id, new IStreams.DoneConnect() {
+ public void doneConnect(IToken token, Exception error) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ // write some data (zeros)
+ // this data can be dropped by Streams since we are not connected yet
+ final byte[] data_out = new byte[rnd.nextInt(10000) + 1000];
+ IStreams.DoneWrite done_write = new IStreams.DoneWrite() {
+ public void doneWrite(IToken token, Exception error) {
+ if (error != null) exit(error);
+ }
+ };
+ int offs = 0;
+ while (offs < data_out.length) {
+ int size = rnd.nextInt(400);
+ if (size > data_out.length - offs) size = data_out.length - offs;
+ streams.write(inp_id, data_out, offs, size, done_write);
+ offs += size;
+ }
+ streams.connect(out_id, new IStreams.DoneConnect() {
+ public void doneConnect(IToken token, Exception error) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ testReadWrite(true, new Runnable() {
+ public void run() {
+ TestStreams.this.inp_id = null;
+ TestStreams.this.out_id = null;
+ subscribe();
+ }
+ });
+ }
+ }
+ });
+ }
+ }
+ });
+ }
+ }
+ });
+ }
+
+ private void subscribe() {
+ streams.subscribe(IDiagnostics.NAME, this, new IStreams.DoneSubscribe() {
+ public void doneSubscribe(IToken token, Exception error) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ createStreams();
+ }
+ }
+ });
+ }
+
+ private void createStreams() {
+ diag.createTestStreams(1153, 947, new IDiagnostics.DoneCreateTestStreams() {
+ public void doneCreateTestStreams(IToken token, Throwable error, String inp_id, String out_id) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ TestStreams.this.inp_id = inp_id;
+ TestStreams.this.out_id = out_id;
+ for (String id : stream_ids) {
+ if (id.equals(inp_id)) continue;
+ if (id.equals(out_id)) continue;
+ streams.disconnect(id, new IStreams.DoneDisconnect() {
+ public void doneDisconnect(IToken token, Exception error) {
+ if (error != null) exit(error);
+ }
+ });
+ }
+ testReadWrite(false, new Runnable() {
+ public void run() {
+ unsubscribe();
+ }
+ });
+ }
+ }
+ });
+ }
+
+ private void testReadWrite(final boolean skip_zeros, final Runnable done) {
+ final byte[] data_out = new byte[rnd.nextInt(10000) + 1000];
+ new Random().nextBytes(data_out);
+ if (skip_zeros) data_out[0] = 1;
+ final HashSet<IToken> cmds = new HashSet<IToken>();
+ IStreams.DoneRead done_read = new IStreams.DoneRead() {
+
+ private int offs = 0;
+ private boolean eos;
+
+ public void doneRead(IToken token, Exception error, int lost_size, byte[] data, boolean eos) {
+ cmds.remove(token);
+ if (error != null) {
+ if (!this.eos) exit(error);
+ }
+ else if (lost_size != 0) {
+ exit(new Exception("Streams service: unexpected data loss"));
+ }
+ else if (this.eos) {
+ if (!eos || data != null && data.length > 0) {
+ exit(new Exception("Streams service: unexpected successful read after EOS"));
+ }
+ }
+ else {
+ if (data != null) {
+ if (offs + data.length > data_out.length) {
+ exit(new Exception("Streams service: read returns more data then expected"));
+ return;
+ }
+ for (int n = 0; n < data.length; n++) {
+ if (!skip_zeros || offs > 0 || data[n] != 0) {
+ if (data[n] != data_out[offs]) {
+ exit(new Exception("Streams service: data error: " + data[n] + " != " + data_out[offs]));
+ return;
+ }
+ offs++;
+ }
+ }
+ }
+ if (eos) {
+ if (offs != data_out.length) {
+ exit(new Exception("Streams service: unexpected EOS"));
+ }
+ this.eos = true;
+ }
+ else if (cmds.size() < 8) {
+ cmds.add(streams.read(out_id, 241, this));
+ }
+ }
+ if (cmds.isEmpty()) disposeStreams(done);
+ }
+ };
+ cmds.add(streams.read(out_id, 223, done_read));
+ cmds.add(streams.read(out_id, 227, done_read));
+ cmds.add(streams.read(out_id, 229, done_read));
+ cmds.add(streams.read(out_id, 233, done_read));
+
+ IStreams.DoneWrite done_write = new IStreams.DoneWrite() {
+ public void doneWrite(IToken token, Exception error) {
+ if (error != null) exit(error);
+ }
+ };
+ int offs = 0;
+ while (offs < data_out.length) {
+ int size = rnd.nextInt(400);
+ if (size > data_out.length - offs) size = data_out.length - offs;
+ streams.write(inp_id, data_out, offs, size, done_write);
+ offs += size;
+ }
+ streams.eos(inp_id, new IStreams.DoneEOS() {
+ public void doneEOS(IToken token, Exception error) {
+ if (error != null) exit(error);
+ }
+ });
+ }
+
+ private void disposeStreams(final Runnable done) {
+ final HashSet<IToken> cmds = new HashSet<IToken>();
+ IStreams.DoneDisconnect done_disconnect = new IStreams.DoneDisconnect() {
+ public void doneDisconnect(IToken token, Exception error) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ cmds.remove(token);
+ if (cmds.isEmpty() && test_suite.isActive(TestStreams.this)) done.run();
+ }
+ }
+ };
+ IDiagnostics.DoneDisposeTestStream done_dispose = new IDiagnostics.DoneDisposeTestStream() {
+ public void doneDisposeTestStream(IToken token, Throwable error) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ cmds.remove(token);
+ if (cmds.isEmpty() && test_suite.isActive(TestStreams.this)) done.run();
+ }
+ }
+ };
+ cmds.add(streams.disconnect(inp_id, done_disconnect));
+ cmds.add(diag.disposeTestStream(inp_id, done_dispose));
+ cmds.add(diag.disposeTestStream(out_id, done_dispose));
+ cmds.add(streams.disconnect(out_id, done_disconnect));
+ }
+
+ private void unsubscribe() {
+ streams.unsubscribe(IDiagnostics.NAME, this, new IStreams.DoneUnsubscribe() {
+ public void doneUnsubscribe(IToken token, Exception error) {
+ if (error != null || test_count >= 10 || System.currentTimeMillis() - start_time >= 4000) {
+ exit(error);
+ }
+ else {
+ test_count++;
+ stream_ids.clear();
+ inp_id = null;
+ out_id = null;
+ connect();
+ }
+ }
+ });
+ }
+
+ private void exit(Throwable x) {
+ if (!test_suite.isActive(this)) return;
+ test_suite.done(this, x);
+ }
+
+ /************************** StreamsListener **************************/
+
+ public void created(String stream_type, String stream_id, String context_id) {
+ if (!IDiagnostics.NAME.equals(stream_type)) exit(new Exception("Invalid stream type in Streams.created event"));
+ if (stream_ids.contains(stream_id)) exit(new Exception("Invalid stream ID in Streams.created event"));
+ stream_ids.add(stream_id);
+ if (inp_id != null) {
+ if (inp_id.equals(stream_id)) exit(new Exception("Invalid stream ID in Streams.created event"));
+ if (out_id.equals(stream_id)) exit(new Exception("Invalid stream ID in Streams.created event"));
+ streams.disconnect(stream_id, new IStreams.DoneDisconnect() {
+ public void doneDisconnect(IToken token, Exception error) {
+ if (error != null) {
+ exit(error);
+ }
+ }
+ });
+ }
+ }
+
+ public void disposed(String stream_type, String stream_id) {
+ if (!IDiagnostics.NAME.equals(stream_type)) exit(new Exception("Invalid stream type in Streams.disposed event"));
+ if (!stream_ids.remove(stream_id)) exit(new Exception("Invalid stream ID in Streams.disposed event"));
+ }
+
+ public boolean canResume(String id) {
+ return true;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestSysMonitor.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestSysMonitor.java
new file mode 100644
index 000000000..74059492c
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestSysMonitor.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (c) 2010, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tm.internal.tcf.debug.tests;
+
+import java.util.HashMap;
+import java.util.HashSet;
+
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IErrorReport;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.services.ISysMonitor;
+import org.eclipse.tm.tcf.services.ISysMonitor.SysMonitorContext;
+
+class TestSysMonitor implements ITCFTest {
+
+ private final TCFTestSuite test_suite;
+ private final ISysMonitor sys_mon;
+ private final HashMap<String,ISysMonitor.SysMonitorContext> procs =
+ new HashMap<String,ISysMonitor.SysMonitorContext>();
+
+ TestSysMonitor(TCFTestSuite test_suite, IChannel channel) {
+ this.test_suite = test_suite;
+ sys_mon = channel.getRemoteService(ISysMonitor.class);
+ }
+
+ public void start() {
+ if (sys_mon == null) {
+ test_suite.done(this, null);
+ }
+ else {
+ sys_mon.getChildren(null, new ISysMonitor.DoneGetChildren() {
+ public void doneGetChildren(IToken token, Exception error, String[] context_ids) {
+ if (error != null) {
+ exit(error);
+ }
+ else if (context_ids == null || context_ids.length == 0) {
+ exit(new Exception("ISysMonitor.getChildren(null) returned empty list"));
+ }
+ else {
+ final HashSet<IToken> cmds = new HashSet<IToken>();
+ for (final String id : context_ids) {
+ cmds.add(sys_mon.getContext(id, new ISysMonitor.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, SysMonitorContext context) {
+ cmds.remove(token);
+ if (error != null) {
+ // Some errors are expected, like "Access Denied"
+ if (!(error instanceof IErrorReport)) {
+ exit(error);
+ return;
+ }
+ }
+ else {
+ procs.put(id, context);
+ }
+ if (cmds.isEmpty()) getEnvironment();
+ }
+ }));
+ }
+ }
+ }
+ });
+ }
+ }
+
+ private void getEnvironment() {
+ final HashSet<IToken> cmds = new HashSet<IToken>();
+ for (final String id : procs.keySet()) {
+ cmds.add(sys_mon.getEnvironment(id, new ISysMonitor.DoneGetEnvironment() {
+ public void doneGetEnvironment(IToken token, Exception error, String[] environment) {
+ cmds.remove(token);
+ if (error != null) {
+ // Some errors are expected, like "Access Denied"
+ if (!(error instanceof IErrorReport)) {
+ exit(error);
+ return;
+ }
+ }
+ if (cmds.isEmpty()) getCommandLine();
+ }
+ }));
+ }
+ }
+
+ private void getCommandLine() {
+ final HashSet<IToken> cmds = new HashSet<IToken>();
+ for (final String id : procs.keySet()) {
+ cmds.add(sys_mon.getCommandLine(id, new ISysMonitor.DoneGetCommandLine() {
+ public void doneGetCommandLine(IToken token, Exception error, String[] cmd_line) {
+ cmds.remove(token);
+ if (error != null) {
+ // Some errors are expected, like "Access Denied"
+ if (!(error instanceof IErrorReport)) {
+ exit(error);
+ return;
+ }
+ }
+ if (cmds.isEmpty()) exit(null);
+ }
+ }));
+ }
+ }
+
+ private void exit(Throwable x) {
+ if (!test_suite.isActive(this)) return;
+ test_suite.done(this, x);
+ }
+
+ public boolean canResume(String id) {
+ return true;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestTerminals.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestTerminals.java
new file mode 100644
index 000000000..42b1ee6ad
--- /dev/null
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestTerminals.java
@@ -0,0 +1,507 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tm.internal.tcf.debug.tests;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.protocol.Protocol;
+import org.eclipse.tm.tcf.services.IProcesses;
+import org.eclipse.tm.tcf.services.IStreams;
+import org.eclipse.tm.tcf.services.ITerminals;
+import org.eclipse.tm.tcf.services.ITerminals.TerminalContext;
+
+class TestTerminals implements ITCFTest {
+
+ private final TCFTestSuite test_suite;
+ private final ITerminals terminals;
+ private final IProcesses processes;
+ private final IStreams streams;
+ private final Random rnd = new Random();
+ private final HashSet<String> stream_ids = new HashSet<String>();
+ private final StringBuffer stdout_buf = new StringBuffer();
+ private final StringBuffer stderr_buf = new StringBuffer();
+ private final HashSet<IToken> disconnect_cmds = new HashSet<IToken>();
+ private final List<String> echo_tx = new ArrayList<String>();
+ private final List<Integer> echo_rx = new ArrayList<Integer>();
+
+ private final int echo_cnt = 50;
+
+ private IStreams.StreamsListener streams_listener;
+ private IStreams.DoneRead stdout_read;
+ private IStreams.DoneRead stderr_read;
+ private String encoding;
+ private TerminalContext terminal;
+ private TerminalContext get_ctx;
+ private Map<String,String> environment;
+ private boolean delay_done;
+ private Collection<Map<String,Object>> signal_list;
+ private IToken get_signals_cmd;
+ private IToken signal_cmd;
+ private boolean signal_sent;
+ private IToken unsubscribe_cmd;
+ private boolean unsubscribe_done;
+ private boolean exited;
+ private boolean stdout_eos;
+ private int time_out = 0;
+
+ private final ITerminals.TerminalsListener listener = new ITerminals.TerminalsListener() {
+
+ public void exited(String id, int exit_code) {
+ if (terminal != null && id.equals(terminal.getID())) {
+ exited = true;
+ if (!signal_sent) {
+ exit(new Exception("Terminal exited with code " + exit_code));
+ }
+ else {
+ run();
+ }
+ }
+ }
+
+ public void winSizeChanged(String id, int w, int h) {
+ }
+ };
+
+ private final IStreams.DoneDisconnect disconnect_done = new IStreams.DoneDisconnect() {
+ public void doneDisconnect(IToken token, Exception error) {
+ assert disconnect_cmds.contains(token);
+ disconnect_cmds.remove(token);
+ if (error != null) exit(error);
+ if (disconnect_cmds.size() == 0 && unsubscribe_done) exit(null);
+ }
+ };
+
+ TestTerminals(TCFTestSuite test_suite, IChannel channel) {
+ this.test_suite = test_suite;
+ terminals = channel.getRemoteService(ITerminals.class);
+ processes = channel.getRemoteService(IProcesses.class);
+ streams = channel.getRemoteService(IStreams.class);
+ }
+
+ public void start() {
+ if (terminals == null || streams == null) {
+ test_suite.done(this, null);
+ }
+ else {
+ terminals.addListener(listener);
+ run();
+ }
+ }
+
+ private void run() {
+ if (environment == null && processes != null) {
+ processes.getEnvironment(new IProcesses.DoneGetEnvironment() {
+ public void doneGetEnvironment(IToken token, Exception error, Map<String, String> environment) {
+ if (error != null) {
+ exit(error);
+ }
+ else if (environment == null) {
+ exit(new Exception("Default process environment must not be null"));
+ }
+ else {
+ TestTerminals.this.environment = environment;
+ run();
+ }
+ }
+ });
+ return;
+ }
+ if (streams_listener == null) {
+ final IStreams.StreamsListener l = new IStreams.StreamsListener() {
+ public void created(String stream_type, String stream_id, String context_id) {
+ if (!terminals.getName().equals(stream_type)) {
+ exit(new Exception("Invalid stream type in Streams.created event: " + stream_type));
+ }
+ else if (stream_id == null || stream_id.length() == 0 || stream_ids.contains(stream_id)) {
+ exit(new Exception("Invalid stream ID in Streams.created event: " + stream_id));
+ }
+ else if (terminal != null) {
+ if (stream_id.equals(terminal.getStdInID()) ||
+ stream_id.equals(terminal.getStdOutID()) ||
+ stream_id.equals(terminal.getStdErrID())) {
+ exit(new Exception("Invalid stream ID in Streams.created event: " + stream_id));
+ }
+ else {
+ disconnect_cmds.add(streams.disconnect(stream_id, disconnect_done));
+ }
+ }
+ else {
+ stream_ids.add(stream_id);
+ }
+ }
+ public void disposed(String stream_type, String stream_id) {
+ }
+ };
+ streams.subscribe(terminals.getName(), l, new IStreams.DoneSubscribe() {
+ public void doneSubscribe(IToken token, Exception error) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ streams_listener = l;
+ run();
+ }
+ }
+ });
+ return;
+ }
+ if (terminal == null) {
+ String[] types = { "ansi", "vt100", null };
+ String[] langs = { "en_US", "en_US.UTF-8", null };
+ String[] env = null;
+ if (environment != null && rnd.nextBoolean()) {
+ int i = 0;
+ env = new String[environment.size() + 1];
+ for (String s : environment.keySet()) {
+ env[i++] = s + "=" + environment.get(s);
+ }
+ env[i++] = "TCF_FOO=BAR";
+ }
+ terminals.launch(types[rnd.nextInt(types.length)], langs[rnd.nextInt(langs.length)], env, new ITerminals.DoneLaunch() {
+ public void doneLaunch(IToken token, Exception error, final TerminalContext terminal) {
+ if (error != null) {
+ exit(error);
+ }
+ else if (terminal == null) {
+ exit(new Exception("Terminal context must not be null"));
+ }
+ else if (terminal.getID() == null) {
+ exit(new Exception("Terminal context ID must not be null"));
+ }
+ else {
+ TestTerminals.this.terminal = terminal;
+ for (Iterator<String> i = stream_ids.iterator(); i.hasNext();) {
+ String stream_id = i.next();
+ if (stream_id.equals(terminal.getStdInID()) ||
+ stream_id.equals(terminal.getStdOutID()) ||
+ stream_id.equals(terminal.getStdErrID())) {
+ // keep connected
+ }
+ else {
+ i.remove();
+ disconnect_cmds.add(streams.disconnect(stream_id, disconnect_done));
+ }
+ }
+ Protocol.invokeLater(100, new Runnable() {
+ public void run() {
+ if (!test_suite.isActive(TestTerminals.this)) return;
+ time_out++;
+ if (test_suite.cancel) {
+ exit(null);
+ }
+ else if (time_out < 200) {
+ Protocol.invokeLater(100, this);
+ }
+ else if (!signal_sent) {
+ exit(new Error("Timeout waiting for terminal reply. Context: " + terminal.getID()));
+ }
+ else if (!exited) {
+ exit(new Error("Timeout waiting for 'Terminals.exited' event. Context: " + terminal.getID()));
+ }
+ else {
+ exit(new Error("Timeout waiting for end-of-stream. Context: " + terminal.getID()));
+ }
+ }
+ });
+ run();
+ }
+ }
+ });
+ return;
+ }
+ if (get_ctx == null) {
+ terminals.getContext(terminal.getID(), new ITerminals.DoneGetContext() {
+ public void doneGetContext(IToken token, Exception error, TerminalContext terminal) {
+ if (error != null) {
+ exit(error);
+ }
+ else if (terminal == null) {
+ exit(new Exception("Terminal context must not be null"));
+ }
+ else if (terminal.getID() == null) {
+ exit(new Exception("Terminal context ID must not be null"));
+ }
+ else if (!TestTerminals.this.terminal.getProperties().equals(terminal.getProperties())) {
+ exit(new Exception("Invalid result of Terminal.getContext"));
+ }
+ else {
+ TestTerminals.this.get_ctx = terminal;
+ run();
+ }
+ }
+ });
+ return;
+ }
+ if (signal_list == null && processes != null && terminal.getProcessID() != null) {
+ assert get_signals_cmd == null;
+ get_signals_cmd = processes.getSignalList(terminal.getProcessID(), new IProcesses.DoneGetSignalList() {
+ public void doneGetSignalList(IToken token, Exception error, Collection<Map<String,Object>> list) {
+ assert get_signals_cmd == token;
+ get_signals_cmd = null;
+ if (error != null) {
+ exit(error);
+ }
+ else if (list == null) {
+ exit(new Exception("Signal list must not be null"));
+ }
+ else {
+ signal_list = list;
+ run();
+ }
+ }
+ });
+ return;
+ }
+ if (encoding == null) {
+ String lang = terminal.getEncoding();
+ if (lang == null && environment != null) lang = environment.get("LC_ALL");
+ if (lang == null && environment != null) lang = environment.get("LANG");
+ if (lang == null) lang = "en_US.UTF-8";
+ int i = lang.indexOf('.');
+ int j = lang.indexOf('@');
+ if (i < 0) {
+ encoding = "UTF-8";
+ }
+ else if (j < i) {
+ encoding = lang.substring(i + 1);
+ }
+ else {
+ encoding = lang.substring(i + 1, j);
+ }
+ }
+ if (stdout_read == null) {
+ final String id = terminal.getStdOutID();
+ if (id == null) {
+ exit(new Exception("stdout stream ID is null"));
+ return;
+ }
+ stdout_read = new IStreams.DoneRead() {
+ public void doneRead(IToken token, Exception error, int lost_size, byte[] data, boolean eos) {
+ if (error != null) {
+ exit(error);
+ }
+ else if (lost_size > 0) {
+ exit(new Exception("Lost bytes in terminal stream"));
+ }
+ else {
+ try {
+ boolean run = false;
+ if (data != null) {
+ stdout_buf.append(new String(data, encoding));
+ if (echo_tx.size() > echo_rx.size()) {
+ String s = echo_tx.get(echo_rx.size());
+ String p = "\n" + s.substring(0, 12);
+ int n = 0;
+ if (echo_rx.size() > 0) n = echo_rx.get(echo_rx.size() - 1);
+ int i = stdout_buf.indexOf(p, n);
+ if (i >= 0 && stdout_buf.length() >= i + s.length() + 4) {
+ time_out = 0;
+ echo_rx.add(i + 1);
+ run = true;
+ }
+ }
+ }
+ if (!eos) {
+ streams.read(id, 0x1000, this);
+ }
+ else {
+ stdout_eos = true;
+ run = true;
+ }
+ if (run) run();
+ }
+ catch (Exception x) {
+ exit(x);
+ }
+ }
+ }
+ };
+ streams.read(id, 0x1000, stdout_read);
+ }
+ if (stderr_read == null && terminal.getStdErrID() != null) {
+ final String id = terminal.getStdErrID();
+ stderr_read = new IStreams.DoneRead() {
+ public void doneRead(IToken token, Exception error, int lost_size, byte[] data, boolean eos) {
+ if (error != null) {
+ exit(error);
+ }
+ else if (lost_size > 0) {
+ exit(new Exception("Lost bytes in terminal stream"));
+ }
+ else {
+ try {
+ if (data != null) stderr_buf.append(new String(data, encoding));
+ if (!eos) streams.read(id, 0x1000, this);
+ }
+ catch (Exception x) {
+ exit(x);
+ }
+ }
+ }
+ };
+ int n = rnd.nextInt(4) + 1;
+ for (int i = 0; i < n; i++) {
+ streams.read(id, 0x1000, stderr_read);
+ }
+ }
+ if (!delay_done) {
+ Protocol.invokeLater(rnd.nextInt(250), new Runnable() {
+ public void run() {
+ delay_done = true;
+ TestTerminals.this.run();
+ }
+ });
+ return;
+ }
+ if (echo_tx.size() < echo_cnt && echo_rx.size() == echo_tx.size()) {
+ try {
+ StringBuffer bf = new StringBuffer();
+ for (int i = 0; i < 0x100; i++) {
+ bf.append((char)('A' + rnd.nextInt('Z' - 'A' + 1)));
+ bf.append((char)('a' + rnd.nextInt('Z' - 'A' + 1)));
+ }
+ String s = bf.toString();
+ echo_tx.add(s);
+ s = "echo " + s + '\n';
+ byte[] buf = s.getBytes(encoding);
+ streams.write(terminal.getStdInID(), buf, 0, buf.length, new IStreams.DoneWrite() {
+ public void doneWrite(IToken token, Exception error) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ run();
+ }
+ }
+ });
+ }
+ catch (Exception x) {
+ exit(x);
+ }
+ return;
+ }
+ if (!exited && !stdout_eos && echo_rx.size() < echo_cnt) return;
+ if (!signal_sent) {
+ assert !exited;
+ if (signal_cmd == null) {
+ int code = 0;
+ if (signal_list != null && rnd.nextBoolean()) {
+ for (Map<String,Object> m : signal_list) {
+ String nm = (String)m.get(IProcesses.SIG_NAME);
+ if (nm != null && nm.equals("SIGKILL")) {
+ Number n = (Number)m.get(IProcesses.SIG_CODE);
+ if (n != null) code = n.intValue();
+ }
+ }
+ if (code == 0) {
+ for (Map<String,Object> m : signal_list) {
+ String nm = (String)m.get(IProcesses.SIG_NAME);
+ if (nm != null && nm.equals("SIGTERM")) {
+ Number n = (Number)m.get(IProcesses.SIG_CODE);
+ if (n != null) code = n.intValue();
+ }
+ }
+ }
+ }
+ if (code > 0) {
+ signal_cmd = processes.signal(terminal.getProcessID(), code, new IProcesses.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ assert signal_cmd == token;
+ signal_cmd = null;
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ signal_sent = true;
+ run();
+ }
+ }
+ });
+ }
+ else {
+ signal_cmd = terminals.exit(terminal.getID(), new ITerminals.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ assert signal_cmd == token;
+ signal_cmd = null;
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ signal_sent = true;
+ run();
+ }
+ }
+ });
+ }
+ }
+ return;
+ }
+ if (exited && stdout_eos) {
+ if (!unsubscribe_done) {
+ if (unsubscribe_cmd == null) {
+ unsubscribe_cmd = streams.unsubscribe(terminals.getName(), streams_listener, new IStreams.DoneUnsubscribe() {
+ public void doneUnsubscribe(IToken token, Exception error) {
+ unsubscribe_done = true;
+ if (error != null) exit(error);
+ else run();
+ }
+ });
+ }
+ return;
+ }
+ else {
+ for (String stream_id : stream_ids) {
+ disconnect_cmds.add(streams.disconnect(stream_id, disconnect_done));
+ }
+ stream_ids.clear();
+ checkTerminalOutput(stdout_buf);
+ checkTerminalOutput(stderr_buf);
+ if (echo_rx.size() < echo_cnt) {
+ exit(new Exception("Terminal exited before test finished"));
+ }
+ else {
+ int n = 0;
+ for (int i : echo_rx) {
+ String s = echo_tx.get(n++);
+ String r = stdout_buf.substring(i, i + s.length());
+ if (!s.equals(r)) {
+ exit(new Exception("Invalid reply: " + r + "\nExpected: " + s));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void checkTerminalOutput(StringBuffer bf) {
+ if (bf.indexOf("Cannot start") >= 0) {
+ exit(new Exception("Unexpected terminal output:\n" + bf));
+ }
+ }
+
+ private void exit(Throwable x) {
+ if (!test_suite.isActive(this)) return;
+ if (terminals != null) terminals.removeListener(listener);
+ test_suite.done(this, x);
+ }
+
+ public boolean canResume(String id) {
+ return true;
+ }
+}

Back to the top