diff options
Diffstat (limited to 'dsf/org.eclipse.cdt.dsf/src/org')
71 files changed, 9507 insertions, 0 deletions
diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ConfinedToDsfExecutor.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ConfinedToDsfExecutor.java new file mode 100644 index 00000000000..27452c25061 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ConfinedToDsfExecutor.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.dsf.concurrent; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation idicating that given package, class, method, or field can be + * access safely only from a DSF executor thread. If declared on package or type, + * a field or method could still be declared with an annotation indicating that it's + * thread-safe. + * <p> + * Note: the runtime retention policy is there to allow automated testing + * and validation code. + * + * @param value The value indicates the method to use to obtain the executor. + * It should be null if it cannot be determined from the given object. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.PACKAGE, ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR}) +@Inherited +@Documented +public @interface ConfinedToDsfExecutor { + String value(); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/CountingRequestMonitor.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/CountingRequestMonitor.java new file mode 100644 index 00000000000..ea9fa9d6c72 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/CountingRequestMonitor.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.dsf.concurrent; + +import java.util.concurrent.Executor; + +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; + +/** + * Utility class to collect multiple request monitor results of commands + * that are initiated simultaneously. The usage is as follows: + * <code><pre> + * final CountingRequestMonitor countingRm = new CountingRequestMonitor(fExecutor, null) { + * public void handleCompleted() { + * System.out.println("All complete, errors=" + !getStatus().isOK()); + * } + * }; + * + * int count = 0; + * for (int i : elements) { + * service.call(i, countingRm); + * count++; + * } + * + * countingRm.setDoneCount(count); + * </pre></code> + */ +public class CountingRequestMonitor extends RequestMonitor { + /** + * Counter tracking the remaining number of times that the done() method + * needs to be called before this request monitor is actually done. + */ + private int fDoneCounter; + + /** + * Flag indicating whether the initial count has been set on this monitor. + */ + private boolean fInitialCountSet = false; + + public CountingRequestMonitor(Executor executor, RequestMonitor parentRequestMonitor) { + super(executor, parentRequestMonitor); + super.setStatus(new MultiStatus(DsfPlugin.PLUGIN_ID, 0, "Collective status for set of sub-operations.", null)); //$NON-NLS-1$ + } + + /** + * Sets the number of times that this request monitor needs to be called + * before this monitor is truly considered done. This method must be called + * exactly once in the life cycle of each counting request monitor. + * @param count Number of times that done() has to be called to mark the request + * monitor as complete. If count is '0', then the counting request monitor is + * marked as done immediately. + */ + public synchronized void setDoneCount(int count) { + assert !fInitialCountSet; + fInitialCountSet = true; + fDoneCounter += count; + if (fDoneCounter <= 0) { + assert fDoneCounter == 0; // Mismatch in the count. + super.done(); + } + } + + /** + * Called to indicate that one of the calls using this monitor is finished. + * Only when <code>done</done> is called the number of times corresponding to the + * count, the request monitor will be considered complete. This method can be + * called before {@link #setDoneCount(int)}. + */ + @Override + public synchronized void done() { + fDoneCounter--; + if (fInitialCountSet && fDoneCounter <= 0) { + assert fDoneCounter == 0; // Mismatch in the count. + super.done(); + } + } + + @Override + public String toString() { + return "CountingRequestMonitor: " + getStatus().toString(); //$NON-NLS-1$ + } + + @Override + public synchronized void setStatus(IStatus status) { + if ((getStatus() instanceof MultiStatus)) { + ((MultiStatus)getStatus()).add(status); + } + }; +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DataRequestMonitor.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DataRequestMonitor.java new file mode 100644 index 00000000000..58daf516459 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DataRequestMonitor.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 Wind River Systems 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.cdt.dsf.concurrent; + +import java.util.concurrent.Executor; + + +/** + * Request monitor that allows data to be returned to the request initiator. + * + * @param V The type of the data object that this monitor handles. + */ +public class DataRequestMonitor<V> extends RequestMonitor { + + /** Data object reference */ + private V fData; + + public DataRequestMonitor(Executor executor, RequestMonitor parentRequestMonitor) { + super(executor, parentRequestMonitor); + } + + /** + * Sets the data object to specified value. To be called by the + * asynchronous method implementor. + * @param data Data value to set. + */ + public synchronized void setData(V data) { fData = data; } + + /** + * Returns the data value, null if not set. + */ + public synchronized V getData() { return fData; } + + @Override + public String toString() { + if (getData() != null) { + return getData().toString(); + } else { + return super.toString(); + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DefaultDsfExecutor.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DefaultDsfExecutor.java new file mode 100644 index 00000000000..e2c7efc5642 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DefaultDsfExecutor.java @@ -0,0 +1,374 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.dsf.concurrent; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; + +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; + +/** + * Default implementation of a DSF executor interfaces, based on the + * standard java.util.concurrent.ThreadPoolExecutor. + */ + +public class DefaultDsfExecutor extends ScheduledThreadPoolExecutor + implements DsfExecutor +{ + /** + * Instance counter for DSF executors. Used in the executor's thread name. + */ + private static int fgInstanceCounter = 0; + + /** + * Name of the executor, used in the executor's thread name. + */ + private String fName; + + /** + * Instance number of this executor, used with the executor name. + */ + private int fInstanceNumber; + + /** Thread factory that creates the single thread to be used for this executor */ + static class DsfThreadFactory implements ThreadFactory { + private String fThreadName; + DsfThreadFactory(String name) { + fThreadName = name; + } + + Thread fThread; + public Thread newThread(Runnable r) { + assert fThread == null; // Should be called only once. + fThread = new Thread(new ThreadGroup(fThreadName), r, fThreadName, 0); + return fThread; + } + } + + public DefaultDsfExecutor() { + this("DSF Executor"); //$NON-NLS-1$ + } + + /** + * Creates a new DSF Executor with the given name. + * @param name Name used to create executor's thread. + */ + public DefaultDsfExecutor(String name) { + super(1, new DsfThreadFactory(name + " - " + fgInstanceCounter)); //$NON-NLS-1$ + fName = name; + fInstanceNumber = fgInstanceCounter++; + + if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) { + // If tracing, pre-start the dispatch thread, and add it to the map. + prestartAllCoreThreads(); + fThreadToExecutorMap.put(((DsfThreadFactory)getThreadFactory()).fThread, DefaultDsfExecutor.this); + } + } + + public boolean isInExecutorThread() { + return Thread.currentThread().equals( ((DsfThreadFactory)getThreadFactory()).fThread ); + } + + protected String getName() { + return fName; + } + + static void logException(Throwable t) { + DsfPlugin plugin = DsfPlugin.getDefault(); + if (plugin == null) return; + + ILog log = plugin.getLog(); + if (log != null) { + log.log(new Status( + IStatus.ERROR, DsfPlugin.PLUGIN_ID, -1, "Uncaught exception in DSF executor thread", t)); //$NON-NLS-1$ + } + // Print out the stack trace to console if assertions are enabled. + if(ASSERTIONS_ENABLED) { + ByteArrayOutputStream outStream = new ByteArrayOutputStream(512); + PrintStream printStream = new PrintStream(outStream); + try { + printStream.write("Uncaught exception in session executor thread: ".getBytes()); //$NON-NLS-1$ + } catch (IOException e2) {} + t.printStackTrace(new PrintStream(outStream)); + System.err.println(outStream.toString()); + } + } + + // + // Utilities used for tracing. + // + protected static boolean DEBUG_EXECUTOR = false; + protected static String DEBUG_EXECUTOR_NAME = ""; //$NON-NLS-1$ + protected static boolean ASSERTIONS_ENABLED = false; + static { + DEBUG_EXECUTOR = DsfPlugin.DEBUG && "true".equals( //$NON-NLS-1$ + Platform.getDebugOption("org.eclipse.cdt.dsf/debug/executor")); //$NON-NLS-1$ + DEBUG_EXECUTOR_NAME = DsfPlugin.DEBUG + ? Platform.getDebugOption("org.eclipse.cdt.dsf/debug/executorName") : ""; //$NON-NLS-1$ //$NON-NLS-2$ + assert (ASSERTIONS_ENABLED = true) == true; + } + + /** + * This map is used by DsfRunnable/Query/DsfCallable to track by which executor + * an executable object was created. + * <br>Note: Only used when tracing. + */ + static Map<Thread, DefaultDsfExecutor> fThreadToExecutorMap = new HashMap<Thread, DefaultDsfExecutor>(); + + /** + * Currently executing runnable/callable. + * <br>Note: Only used when tracing. + */ + TracingWrapper fCurrentlyExecuting; + + /** + * Counter number saved by each tracing runnable when executed + * <br>Note: Only used when tracing. + */ + int fSequenceCounter; + + /** + * Wrapper for runnables/callables, is used to store tracing information + * <br>Note: Only used when tracing. + */ + abstract class TracingWrapper { + /** Sequence number of this runnable/callable */ + int fSequenceNumber = -1; + + /** Trace of where the runnable/callable was submitted to the executor */ + StackTraceWrapper fSubmittedAt = null; + + /** Reference to the runnable/callable that submitted this runnable/callable to the executor */ + TracingWrapper fSubmittedBy = null; + + /** + * @param offset the number of items in the stack trace not to be printed + */ + TracingWrapper(int offset) { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + // guard against the offset being greater than the stack trace + offset = Math.min(offset, stackTrace.length); + fSubmittedAt = new StackTraceWrapper(new StackTraceElement[stackTrace.length - offset]); + System.arraycopy(stackTrace, offset - 1, fSubmittedAt.fStackTraceElements, 0, fSubmittedAt.fStackTraceElements.length); + if (isInExecutorThread() && fCurrentlyExecuting != null) { + fSubmittedBy = fCurrentlyExecuting; + } + } + + void traceExecution() { + fSequenceNumber = fSequenceCounter++; + fCurrentlyExecuting = this; + + // Write to console only if tracing is enabled (as opposed to tracing or assertions). + if (DEBUG_EXECUTOR && ("".equals(DEBUG_EXECUTOR_NAME) || fName.equals(DEBUG_EXECUTOR_NAME))) { //$NON-NLS-1$ + StringBuilder traceBuilder = new StringBuilder(); + + // Record the time + traceBuilder.append(DsfPlugin.getDebugTime()); + traceBuilder.append(' '); + + // Record the executor # + traceBuilder.append('#'); + traceBuilder.append(fSequenceNumber); + + // Record the executor name + traceBuilder.append('('); + traceBuilder.append(fName); + traceBuilder.append(" - "); //$NON-NLS-1$ + traceBuilder.append(fInstanceNumber); + traceBuilder.append(')'); + traceBuilder.append(' '); + + // Append executable class name + traceBuilder.append(getExecutable().getClass().getName()); + + // Add executable's toString(). + traceBuilder.append("\n "); //$NON-NLS-1$ + traceBuilder.append(getExecutable().toString()); + + // Append "create by" info. + if (getExecutable() instanceof DsfExecutable) { + DsfExecutable dsfExecutable = (DsfExecutable)getExecutable(); + if (dsfExecutable.fCreatedAt != null || dsfExecutable.fCreatedBy != null) { + traceBuilder.append("\n created "); //$NON-NLS-1$ + if (dsfExecutable.fCreatedBy != null) { + traceBuilder.append(" by #"); //$NON-NLS-1$ + traceBuilder.append(dsfExecutable.fCreatedBy.fSequenceNumber); + } + if (dsfExecutable.fCreatedAt != null) { + traceBuilder.append("\n at "); //$NON-NLS-1$ + traceBuilder.append(dsfExecutable.fCreatedAt.fStackTraceElements[0].toString()); + for (int i = 1; i < dsfExecutable.fCreatedAt.fStackTraceElements.length && i < 3; i++) { + traceBuilder.append("\n "); //$NON-NLS-1$ + traceBuilder.append(dsfExecutable.fCreatedAt.fStackTraceElements[i].toString()); + } + } + } + } + + // Submitted info + traceBuilder.append("\n submitted"); //$NON-NLS-1$ + if (fSubmittedBy != null) { + traceBuilder.append(" by #"); //$NON-NLS-1$ + traceBuilder.append(fSubmittedBy.fSequenceNumber); + } + traceBuilder.append("\n at "); //$NON-NLS-1$ + traceBuilder.append(fSubmittedAt.fStackTraceElements[0].toString()); + for (int i = 1; i < fSubmittedAt.fStackTraceElements.length && i < 3; i++) { + traceBuilder.append("\n "); //$NON-NLS-1$ + traceBuilder.append(fSubmittedAt.fStackTraceElements[i].toString()); + } + traceBuilder.append(" at "); //$NON-NLS-1$ + traceBuilder.append(fSubmittedAt.fStackTraceElements[0].toString()); + + // Finally write out to console + DsfPlugin.debug(traceBuilder.toString()); + } + } + + abstract protected Object getExecutable(); + } + + + class TracingWrapperRunnable extends TracingWrapper implements Runnable { + final Runnable fRunnable; + public TracingWrapperRunnable(Runnable runnable, int offset) { + super(offset); + if (runnable == null) throw new NullPointerException(); + fRunnable = runnable; + + // Check if executable wasn't executed already. + if (DEBUG_EXECUTOR && fRunnable instanceof DsfExecutable) { + assert !((DsfExecutable)fRunnable).getSubmitted() : "Executable was previously executed."; //$NON-NLS-1$ + ((DsfExecutable)fRunnable).setSubmitted(); + } + } + + @Override + protected Object getExecutable() { return fRunnable; } + + public void run() { + traceExecution(); + + // Finally invoke the runnable code. + try { + fRunnable.run(); + } catch (RuntimeException e) { + // If an exception was thrown in the Runnable, trace it. + // Because there is no one else to catch it, it is a + // programming error. + logException(e); + throw e; + } + } + } + + public class TracingWrapperCallable<T> extends TracingWrapper implements Callable<T> { + final Callable<T> fCallable; + public TracingWrapperCallable(Callable<T> callable, int offset) { + super(offset); + if (callable == null) throw new NullPointerException(); + fCallable = callable; + } + + @Override + protected Object getExecutable() { return fCallable; } + + public T call() throws Exception { + traceExecution(); + + // Finally invoke the runnable code. + // Note that callables can throw exceptions that can be caught + // by clients that invoked them using ExecutionException. + return fCallable.call(); + } + } + + @Override + public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { + if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) { + if ( !(callable instanceof TracingWrapper) ) { + callable = new TracingWrapperCallable<V>(callable, 6); + } + } + return super.schedule(callable, delay, unit); + } + @Override + public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { + if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) { + if ( !(command instanceof TracingWrapper) ) { + command = new TracingWrapperRunnable(command, 6); + } + } + return super.schedule(command, delay, unit); + } + + @Override + public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { + if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) { + command = new TracingWrapperRunnable(command, 6); + } + return super.scheduleAtFixedRate(command, initialDelay, period, unit); + } + + @Override + public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { + if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) { + command = new TracingWrapperRunnable(command, 6); + } + return super.scheduleWithFixedDelay(command, initialDelay, delay, unit); + } + + @Override + public void execute(Runnable command) { + if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) { + command = new TracingWrapperRunnable(command, 6); + } + super.execute(command); + } + + @Override + public Future<?> submit(Runnable command) { + if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) { + command = new TracingWrapperRunnable(command, 6); + } + return super.submit(command); + } + + @Override + public <T> Future<T> submit(Callable<T> callable) { + if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) { + callable = new TracingWrapperCallable<T>(callable, 6); + } + return super.submit(callable); + } + + @Override + public <T> Future<T> submit(Runnable command, T result) { + if(DEBUG_EXECUTOR || ASSERTIONS_ENABLED) { + command = new TracingWrapperRunnable(command, 6); + } + return super.submit(command, result); + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DsfExecutable.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DsfExecutable.java new file mode 100644 index 00000000000..52d1d43dd25 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DsfExecutable.java @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.dsf.concurrent; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.core.runtime.Platform; + +/** + * Base class for DSF-instrumented alternative to the Runnable/Callable interfaces. + * <p> + * While it is perfectly fine for clients to call the DSF executor with + * an object only implementing the Runnable/Callable interface, the DsfExecutable + * contains fields and methods that used for debugging and tracing when + * tracing is enabled. + */ +@Immutable +public class DsfExecutable { + /** + * Flag indicating that tracing of the DSF executor is enabled. It enables + * storing of the "creator" information as well as tracing of disposed + * runnables that have not been submitted to the executor. + */ + static boolean DEBUG_EXECUTOR = false; + + /** + * Flag indicating that assertions are enabled. It enables storing of the + * "creator" executable for debugging purposes. + */ + static boolean ASSERTIONS_ENABLED = false; + + static { + assert (ASSERTIONS_ENABLED = true) == true; + DEBUG_EXECUTOR = DsfPlugin.DEBUG && "true".equals( //$NON-NLS-1$ + Platform.getDebugOption("org.eclipse.cdt.dsf/debug/executor")); //$NON-NLS-1$ + } + + /** + * Field that holds the stack trace of where this executable was created. + * Used for tracing and debugging only. + */ + final StackTraceWrapper fCreatedAt; + + /** + * Field holding the reference of the executable that created this + * executable. Used for tracing only. + */ + final DefaultDsfExecutor.TracingWrapper fCreatedBy; + + /** + * Flag indicating whether this executable was ever executed by an + * executor. Used for tracing only. + */ + private volatile boolean fSubmitted = false; + + @SuppressWarnings("unchecked") + public DsfExecutable() { + // Use assertion flag (-ea) to jre to avoid affecting performance when not debugging. + if (ASSERTIONS_ENABLED || DEBUG_EXECUTOR) { + // Find the runnable/callable that is currently running. + DefaultDsfExecutor executor = DefaultDsfExecutor.fThreadToExecutorMap.get(Thread.currentThread()); + if (executor != null) { + fCreatedBy = executor.fCurrentlyExecuting; + } else { + fCreatedBy = null; + } + + // Get the stack trace and find the first method that is not a + // constructor of this object. + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + Class thisClass = getClass(); + Set<String> classNamesSet = new HashSet<String>(); + while(thisClass != null) { + classNamesSet.add(thisClass.getName()); + thisClass = thisClass.getSuperclass(); + } + int i; + for (i = 3; i < stackTrace.length; i++) { + if ( !classNamesSet.contains(stackTrace[i].getClassName()) ) break; + } + fCreatedAt = new StackTraceWrapper(new StackTraceElement[stackTrace.length - i]); + System.arraycopy(stackTrace, i, fCreatedAt.fStackTraceElements, 0, fCreatedAt.fStackTraceElements.length); + } else { + fCreatedAt = null; + fCreatedBy = null; + } + } + + public boolean getSubmitted() { + return fSubmitted; + } + + /** + * Marks this executable to indicate that it has been executed by the + * executor. To be invoked only by DsfExecutor. + */ + public void setSubmitted() { + fSubmitted = true; + } + + /** + * Returns whether the runnable/callable is expected to be always executed. + * Overriding classes can implement this method and return false, to avoid + * unnecessary trace output. + * @return true if this runnable is expected to run. + */ + protected boolean isExecutionRequired() { + return true; + } + + @Override + protected void finalize() { + if (DEBUG_EXECUTOR && !fSubmitted && isExecutionRequired()) { + StringBuilder traceBuilder = new StringBuilder(); + + // Record the time + traceBuilder.append(DsfPlugin.getDebugTime()); + traceBuilder.append(' '); + + // Record the event + traceBuilder.append("DsfExecutable was never executed:\n "); //$NON-NLS-1$ + traceBuilder.append(this); + traceBuilder.append("\nCreated at:"); //$NON-NLS-1$ + traceBuilder.append(fCreatedAt); + + DsfPlugin.debug(traceBuilder.toString()); + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DsfExecutor.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DsfExecutor.java new file mode 100644 index 00000000000..8c7cd554bc2 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DsfExecutor.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.dsf.concurrent; + +import java.util.concurrent.ScheduledExecutorService; + +/** + * DSF executor service. Implementations of this executor must ensure + * that all runnables and callables are executed in the same thread: the + * executor's single dispatch thread. + * <br>Note: A DSF executor dispatch thread does not necessarily have + * to be exclusive to the executor, it could be shared with + * another event dispatch service, such as the SWT display dispatch thread. + */ +@ThreadSafe +public interface DsfExecutor extends ScheduledExecutorService +{ + /** + * Checks if the thread that this method is called in is the same as the + * executor's dispatch thread. + * @return true if in DSF executor's dispatch thread + */ + public boolean isInExecutorThread(); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DsfRunnable.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DsfRunnable.java new file mode 100644 index 00000000000..f315288668a --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/DsfRunnable.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems 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.cdt.dsf.concurrent; + + +/** + * A DSF-instrumented alternative to the Runnable interface. + * <p> + * While it is perfectly fine for clients to call the DSF executor with + * an object only implementing the Runnable interface, the DsfRunnable + * contains fields and methods that used for debugging and tracing when + * tracing is enabled. + */ +abstract public class DsfRunnable extends DsfExecutable implements Runnable { +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/IDsfStatusConstants.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/IDsfStatusConstants.java new file mode 100644 index 00000000000..d7be9a7ad67 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/IDsfStatusConstants.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems 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.cdt.dsf.concurrent; + +/** + * Interface that hold the codes used when reporting status using the DSF + * Request Monitor. + */ +public interface IDsfStatusConstants { + /** + * Error code indicating that the service is in a state which does not allow the + * request to be processed. For example if the client requested target information + * after target was disconnected. + */ + final static int INVALID_STATE = 10001; + + /** + * Error code indicating that client supplied an invalid handle to the service. + * A handle could become invalid after an object it represents is removed from + * the system. + */ + final static int INVALID_HANDLE = 10002; + + /** + * Error code indicating that the client request is not supported/implemented. + */ + final static int NOT_SUPPORTED = 10003; + + /** + * Error code indicating that the request to a sub-service or an external process + * failed. + */ + final static int REQUEST_FAILED = 10004; + + /** + * Error code indicating an unexpected condition in the service, i.e. programming error. + */ + final static int INTERNAL_ERROR = 10005; + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ImmediateExecutor.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ImmediateExecutor.java new file mode 100644 index 00000000000..6176514e775 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ImmediateExecutor.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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.cdt.dsf.concurrent; + +import java.util.concurrent.Executor; + +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.core.runtime.Platform; + +/** + * Executor that executes a runnable immediately as it is submitted. This + * executor is useful for clients that need to create <code>RequestMonitor</code> + * objects, but which do not have their own executor. + * @see RequestMonitor + */ +public class ImmediateExecutor implements Executor { + + /** + * Debug flag used for tracking runnables that were never executed, + * or executed multiple times. + */ + protected static boolean DEBUG_EXECUTOR = false; + static { + DEBUG_EXECUTOR = DsfPlugin.DEBUG && "true".equals( //$NON-NLS-1$ + Platform.getDebugOption("org.eclipse.cdt.dsf/debug/executor")); //$NON-NLS-1$ + } + + private static ImmediateExecutor fInstance = new ImmediateExecutor(); + + /** + * The default constructor is hidden. {@link #getInstance()} should be + * used instead. + */ + private ImmediateExecutor() {} + + /** + * Returns the singleton instance of ImmediateExecutor. + */ + public static Executor getInstance() { + return fInstance; + } + + public void execute(Runnable command) { + // Check if executable wasn't executed already. + if (DEBUG_EXECUTOR && command instanceof DsfExecutable) { + assert !((DsfExecutable)command).getSubmitted() : "Executable was previously executed."; //$NON-NLS-1$ + ((DsfExecutable)command).setSubmitted(); + } + command.run(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Immutable.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Immutable.java new file mode 100644 index 00000000000..a336b893792 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Immutable.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems 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.cdt.dsf.concurrent; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation idicating that given class is immutable and thus thread-safe. + * This annotation is not automatically inherited by sub-classes, since + * sub-classes need to make sure that they are immutable as well. + * <p> + * Note: the runtime retention policy is there to allow automated testing + * and validation code. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Documented +public @interface Immutable { + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/MultiRequestMonitor.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/MultiRequestMonitor.java new file mode 100644 index 00000000000..ba127835243 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/MultiRequestMonitor.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.dsf.concurrent; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; + +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.core.runtime.MultiStatus; + +/** + * Utility class to collect multiple request monitor results of commands + * that are initiated simultaneously. The usage is as follows: + * <pre> + * final MultiRequestMonitor multiRequestMon = new MultiRequestMonitor(fExecutor, null) { + * public void handleCompleted() { + * System.out.println("All complete, errors=" + !getStatus().isOK()); + * } + * }; + * + * for (int i = 0; i < 10; i++) { + * service.call(i, multiRequestMon.addRequestMonitor( + * new RequestMonitor(fExecutor, null) { + * public void handleCompleted() { + * System.out.println(Integer.toString(i) + " complete"); + * multiRequestMon.requestMonitorDone(this); + * } + * })); + * } + * </pre> + */ +public class MultiRequestMonitor<V extends RequestMonitor> extends RequestMonitor { + private List<V> fRequestMonitorList = new LinkedList<V>(); + private Map<V,Boolean> fStatusMap = new HashMap<V,Boolean>(); + private int fDoneCounter; + + public MultiRequestMonitor(Executor executor, RequestMonitor parentRequestMonitor) { + super(executor, parentRequestMonitor); + setStatus(new MultiStatus(DsfPlugin.PLUGIN_ID, 0, "Collective status for set of sub-operations.", null)); //$NON-NLS-1$ + } + + /** + * Adds a new RequestMonitor callback to this tracker's list. + * @param <T> Client-specific class of the RequestMonitor callback, it's used here to avoid an + * unnecessary cast by the client. + * @param rm Request monitor object to add to the tracker + * @return The request monitor that was just added, it allows this method to be used + * inlined in service method calls + */ + public <T extends V> T add(T rm) { + assert !fStatusMap.containsKey(rm); + fRequestMonitorList.add(rm); + fStatusMap.put(rm, false); + fDoneCounter++; + return rm; + } + + /** + * Marks the given RequestMonitor callback as completed. Client implementations of + * the RequestMonitor callback have to call this method in order for the tracker + * to complete. + * <br> + * @param requestMonitor + */ + public synchronized void requestMonitorDone(V requestMonitor) { + if (getStatus() instanceof MultiStatus) { + ((MultiStatus)getStatus()).merge(requestMonitor.getStatus()); + } + assert fStatusMap.containsKey(requestMonitor); + fStatusMap.put(requestMonitor, true); + assert fDoneCounter > 0; + fDoneCounter--; + if (fDoneCounter == 0) { + assert !fStatusMap.containsValue(false); + super.done(); + } + } + + /** + * Returns the list of requested monitors, sorted in order as they were added. + */ + public List<V> getRequestMonitors() { + return fRequestMonitorList; + } + + /** + * Returns true if given monitor is finished. + */ + public boolean isRequestMonitorDone(V rm) { + return fStatusMap.get(rm); + } + + @Override + public String toString() { + return "Multi-RequestMonitor: " + getStatus().toString(); //$NON-NLS-1$ + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Query.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Query.java new file mode 100644 index 00000000000..eff2c70c5b4 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Query.java @@ -0,0 +1,220 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.dsf.concurrent; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.AbstractQueuedSynchronizer; + +import org.eclipse.core.runtime.CoreException; + + +/** + * A convenience class that allows a client to retrieve data from services + * synchronously from a non-dispatch thread. This class is different from + * a Callable<V> in that it allows the implementation code to calculate + * the result in several dispatches, rather than requiring it to return the + * data at end of Callable#call method. + * <p> + * Usage:<br/> + * <pre> + * class DataQuery extends Query<Data> { + * protected void execute(DataRequestMonitor<Data> rm) { + * rm.setData(fSlowService.getData()); + * rm.done(); + * } + * } + * + * DsfExecutor executor = getExecutor(); + * DataQuery query = new DataQuery(); + * executor.submit(query); + * + * try { + * Data data = query.get(); + * } + * + * </pre> + * <p> + * @see java.util.concurrent.Callable + */ +@ThreadSafe +abstract public class Query<V> extends DsfRunnable + implements Future<V> +{ + /** The synchronization object for this query */ + private final Sync fSync = new Sync(); + + /** + * The Query constructor no longer requires an executor to be specified. + * This executor was used to contruct the DataRequestMonitor argument to the + * {@link #execute(DataRequestMonitor)} method. But a simplification in the + * RequestMonitor object, made this unnecessary. + * @param executor + */ + @Deprecated + public Query(DsfExecutor executor) { + } + + /** + * The no-argument constructor + */ + public Query() {} + + public V get() throws InterruptedException, ExecutionException { return fSync.doGet(); } + + public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return fSync.doGet(unit.toNanos(timeout)); + } + + /** + * Don't try to interrupt the DSF executor thread, just ignore the request + * if set. + */ + public boolean cancel(boolean mayInterruptIfRunning) { + return fSync.doCancel(); + } + + public boolean isCancelled() { return fSync.doIsCancelled(); } + + public boolean isDone() { return fSync.doIsDone(); } + + + protected void doneException(Throwable t) { + fSync.doSetException(t); + } + + abstract protected void execute(DataRequestMonitor<V> rm); + + public void run() { + if (fSync.doRun()) { + try { + /* + * Create the executor which is going to handle the completion of the + * request monitor. Normally a DSF executor is supplied here which + * causes the request monitor to be invoked in a new dispatch loop. + * But since the query is a synchronization object, it can handle + * the completion of the request in any thread. + * Avoiding the use of a DSF executor is very useful because queries are + * meant to be used by clients calling from non-dispatch thread, and there + * is a chance that a client may execute a query just as a session is being + * shut down. In that case, the DSF executor may throw a + * RejectedExecutionException which would have to be handled by the query. + */ + execute(new DataRequestMonitor<V>(ImmediateExecutor.getInstance(), null) { + @Override + public void handleCompleted() { + if (isSuccess()) fSync.doSet(getData()); + else fSync.doSetException(new CoreException(getStatus())); + } + }); + } catch(Throwable t) { + /* + * Catching the exception here will only work if the exception + * happens within the execute. It will not work in cases when + * the execute submits other runnables, and the other runnables + * encounter the exception. + */ + fSync.doSetException(t); + + /* + * Since we caught the exception, it will not be logged by + * DefaultDsfExecutable.afterExecution(). So log it here. + */ + DefaultDsfExecutor.logException(t); + } + } + } + + @SuppressWarnings("serial") + final class Sync extends AbstractQueuedSynchronizer { + private static final int STATE_RUNNING = 1; + private static final int STATE_DONE = 2; + private static final int STATE_CANCELLED = 4; + + private V fResult; + private Throwable fException; + + private boolean ranOrCancelled(int state) { + return (state & (STATE_DONE | STATE_CANCELLED)) != 0; + } + + @Override + protected int tryAcquireShared(int ignore) { + return doIsDone()? 1 : -1; + } + + @Override + protected boolean tryReleaseShared(int ignore) { + return true; + } + + boolean doIsCancelled() { + return getState() == STATE_CANCELLED; + } + + boolean doIsDone() { + return ranOrCancelled(getState()); + } + + V doGet() throws InterruptedException, ExecutionException { + acquireSharedInterruptibly(0); + if (getState() == STATE_CANCELLED) throw new CancellationException(); + if (fException != null) throw new ExecutionException(fException); + return fResult; + } + + V doGet(long nanosTimeout) throws InterruptedException, ExecutionException, TimeoutException { + if (!tryAcquireSharedNanos(0, nanosTimeout)) throw new TimeoutException(); + if (getState() == STATE_CANCELLED) throw new CancellationException(); + if (fException != null) throw new ExecutionException(fException); + return fResult; + } + + void doSet(V v) { + while(true) { + int s = getState(); + if (ranOrCancelled(s)) return; + if (compareAndSetState(s, STATE_DONE)) break; + } + fResult = v; + releaseShared(0); + } + + void doSetException(Throwable t) { + while(true) { + int s = getState(); + if (ranOrCancelled(s)) return; + if (compareAndSetState(s, STATE_DONE)) break; + } + fException = t; + fResult = null; + releaseShared(0); + } + + boolean doCancel() { + while(true) { + int s = getState(); + if (ranOrCancelled(s)) return false; + if (compareAndSetState(s, STATE_CANCELLED)) break; + } + releaseShared(0); + return true; + } + + boolean doRun() { + return compareAndSetState(0, STATE_RUNNING); + } + } +} + diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/RequestMonitor.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/RequestMonitor.java new file mode 100644 index 00000000000..ac5b466e94e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/RequestMonitor.java @@ -0,0 +1,401 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.dsf.concurrent; + +import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Status; + +/** + * Used to monitor the result of an asynchronous request. Because of the + * asynchronous nature of DSF code, a very large number of methods needs to + * signal the result of an operation through a call-back. This class is the base + * class for such call backs. + * <p> + * The intended use of this class, is that a client who is calling an asynchronous + * method, will sub-class RequestMonitor, and implement the method {@link #handleCompleted()}, + * or any of the other <code>handle...</code> methods, in order to interpret the + * results of the request. The object implementing the asynchronous method is required + * to call the {@link #done()} method on the request monitor object that it received + * as an argument. + * </p> + * <p> + * The severity of the {@link IStatus> returned by #getStatus() can be used to + * determine the success or failure of the asynchronous operation. By convention + * the error codes returned by asynchronous method should be interpreted as follows: + * <ul> + * <li>OK and INFO - Result is a success. In DataRequestMonitor, getData() should + * return a value.</li> + * <li>WARNING - Acceptable error condition (getData() may return null). Where for + * example user tried to retrieve variable data, but the program resumed in the + * mean time and an event will be generated shortly which will clear the variables + * view.</li> + * <li>ERROR - An error condition that should probably be reported to the user.</li> + * <li>CANCEL - The request was canceled, and the asynchronous method was not + * completed.</li> + * </ul> + * </p> + * <p> + * The RequestMonitor constructor accepts an optional "parent" request monitor. If a + * parent monitor is specified, it will automatically be invoked by this monitor when + * the request is completed. The parent option is useful when implementing a method + * which is asynchronous (and accepts a request monitor as an argument) and which itself + * calls another asynchronous method to complete its operation. For example, in the + * request monitor implementation below, the implementation only needs to override + * <code>handleOK()</code>, because the base implementation will handle notifying the + * parent <code>rm</code> in case the <code>getIngredients()</code> call fails. + * <pre> + * public void createCupCakes(final DataRequestMonitor<CupCake[]> rm) { + * getIngredients(new DataRequestMonitor<Ingredients>(fExecutor, rm) { + * public void handleOK() { + * rm.setData( new CupCake(getData().getFlour(), getData().getSugar(), + * getData().getBakingPowder())); + * rm.done(); + * } + * }); + * } + * </pre> + * </p> + */ +@ThreadSafe +public class RequestMonitor { + + /** + * Interface used by RequestMonitor to notify when a given request monitor + * is canceled. + * + * @see RequestMonitor + */ + public static interface ICanceledListener { + + /** + * Called when the given request monitor is canceled. + */ + public void requestCanceled(RequestMonitor rm); + } + + /** + * The executor that will be used in order to invoke the handler of the results + * of the request. + */ + private final Executor fExecutor; + + /** + * The request monitor which was used to call into the method that created this + * monitor. + */ + private final RequestMonitor fParentRequestMonitor; + + private ListenerList fCancelListeners; + + /** + * Status + */ + private IStatus fStatus = Status.OK_STATUS; + private boolean fCanceled = false; + private boolean fDone = false; + + /** + * Constructor with an optional parent monitor. + * @param executor This executor will be used to invoke the runnable that + * will allow processing the completion code of this request monitor. + * @param parentRequestMonitor The optional parent request monitor to be invoked by + * default when this request completes. Parameter may be null. + */ + public RequestMonitor(Executor executor, RequestMonitor parentRequestMonitor) { + fExecutor = executor; + fParentRequestMonitor = parentRequestMonitor; + + // If the parent rm is not null, add ourselves as a listener so that + // this request monitor will automatically be canceled when the parent + // is canceled. + if (fParentRequestMonitor != null) { + fParentRequestMonitor.addCancelListener( + new ICanceledListener() { + public void requestCanceled(RequestMonitor rm) { + cancel(); + } + }); + } + } + + /** + * Sets the status of the result of the request. If status is OK, this + * method does not need to be called. + */ + public synchronized void setStatus(IStatus status) { + assert isCanceled() || status.getSeverity() != IStatus.CANCEL; + fStatus = status; + } + + /** Returns the status of the completed method. */ + public synchronized IStatus getStatus() { + if (isCanceled()) { + return Status.CANCEL_STATUS; + } + return fStatus; + } + + /** + * Sets this request monitor as canceled and calls the cancel listeners if any. + * <p> + * Note: Calling cancel() does not automatically complete the RequestMonitor. + * The asynchronous call still has to call done(). + * </p> + * <p> + * Note: logically a request should only be canceled by the client that issued + * the request in the first place. After a request is canceled, the method + * that is fulfilling the request may call {@link #setStatus(IStatus)} with + * severity of <code>IStatus.CANCEL</code> to indicate that it recognized that + * the given request was canceled and it did not perform the given operation. + * </p> + */ + public void cancel() { + Object[] listeners = null; + synchronized (this) { + // Check to make sure the request monitor wasn't previously canceled. + if (!fCanceled) { + fCanceled = true; + if (fCancelListeners != null) { + listeners = fCancelListeners.getListeners(); + } + } + } + + // Call the listeners outside of a synchronized section to reduce the + // risk of deadlocks. + if (listeners != null) { + for (Object listener : listeners) { + ((ICanceledListener)listener).requestCanceled(this); + } + } + } + + /** + * Returns whether the request was canceled. Even if the request is + * canceled by the client, the implementor handling the request should + * still call {@link #done()} in order to complete handling + * of the request monitor. + */ + public synchronized boolean isCanceled() { + return fCanceled || (fParentRequestMonitor != null && fParentRequestMonitor.isCanceled()); + } + + /** + * Adds the given listener to list of listeners that are notified when this + * request monitor is canceled. + */ + public synchronized void addCancelListener(ICanceledListener listener) { + if (fCancelListeners == null) { + fCancelListeners = new ListenerList(); + } + fCancelListeners.add(listener); + } + + /** + * Removes the given listener from the list of listeners that are notified + * when this request monitor is canceled. + */ + public synchronized void removeCancelListener(ICanceledListener listener) { + if (fCancelListeners != null) { + fCancelListeners.remove(listener); + } + } + + /** + * Marks this request as completed. Once this method is called, the + * monitor submits a runnable to the DSF Executor to call the + * <code>handle...</code> methods. + * <p> + * Note: This method should be called once and only once, for every request + * issued. Even if the request was canceled. + * </p> + */ + public synchronized void done() { + if (fDone) { + throw new IllegalStateException("RequestMonitor: " + this + ", done() method called more than once"); //$NON-NLS-1$//$NON-NLS-2$ + } + fDone = true; + try { + fExecutor.execute(new DsfRunnable() { + public void run() { + RequestMonitor.this.handleCompleted(); + } + @Override + public String toString() { + return "Completed: " + RequestMonitor.this.toString(); //$NON-NLS-1$ + } + }); + } catch (RejectedExecutionException e) { + handleRejectedExecutionException(); + } + } + + @Override + public String toString() { + return "RequestMonitor (" + super.toString() + "): " + getStatus().toString(); //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Checks whether the given request monitor completed with success or + * failure result. If the request monitor was canceled it is considered + * that it failed, regardless of the status. + */ + public boolean isSuccess() { + return !isCanceled() && getStatus().getSeverity() <= IStatus.INFO; + } + + /** + * Default handler for the completion of a request. The implementation + * calls {@link #handleSuccess()} if the request succeeded, and calls + * {@link #handleFailure()} or cancel otherwise. + * <br> + * Note: Sub-classes may override this method. + */ + @ConfinedToDsfExecutor("fExecutor") + protected void handleCompleted() { + if (isSuccess()) { + handleSuccess(); + } else { + handleFailure(); + } + } + + /** + * Default handler for a successful the completion of a request. If this + * monitor has a parent monitor that was configured by the constructor, that + * parent monitor is notified. Otherwise this method does nothing. + * {@link #handleFailure()} or cancel otherwise. + * <br> + * Note: Sub-classes may override this method. + */ + @ConfinedToDsfExecutor("fExecutor") + protected void handleSuccess() { + if (fParentRequestMonitor != null) { + fParentRequestMonitor.done(); + } + } + + /** + * The default implementation of a cancellation or an error result of a + * request. The implementation delegates to {@link #handleCancel()} and + * {@link #handleErrorOrWarning()} as needed. + * <br> + * Note: Sub-classes may override this method. + */ + @ConfinedToDsfExecutor("fExecutor") + protected void handleFailure() { + assert !isSuccess(); + + if (isCanceled()) { + handleCancel(); + } else { + if (getStatus().getSeverity() == IStatus.CANCEL) { + DsfPlugin.getDefault().getLog().log(new Status( + IStatus.ERROR, DsfPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Request monitor: '" + this + "' resulted in a cancel status: " + getStatus() + ", even though the request is not set to cancel.", null)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + handleErrorOrWarning(); + } + } + + /** + * The default implementation of an error or warning result of a request. + * The implementation delegates to {@link #handleError()} and + * {@link #handleWarning()} as needed. + * <br> + * Note: Sub-classes may override this method. + */ + @ConfinedToDsfExecutor("fExecutor") + protected void handleErrorOrWarning() { + if (getStatus().getSeverity() == IStatus.ERROR) { + handleError(); + } else { + handleWarning(); + } + } + + /** + * The default implementation of an error result of a request. If this + * monitor has a parent monitor that was configured by the constructor, that + * parent monitor is configured with a new status containing this error. + * Otherwise the error is logged. + * <br> + * Note: Sub-classes may override this method. + */ + @ConfinedToDsfExecutor("fExecutor") + protected void handleError() { + if (fParentRequestMonitor != null) { + fParentRequestMonitor.setStatus(getStatus()); + fParentRequestMonitor.done(); + } else { + MultiStatus logStatus = new MultiStatus(DsfPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Request for monitor: '" + toString() + "' resulted in an error.", null); //$NON-NLS-1$ //$NON-NLS-2$ + logStatus.merge(getStatus()); + DsfPlugin.getDefault().getLog().log(logStatus); + } + } + + /** + * The default implementation of an error result of a request. If this + * monitor has a parent monitor that was configured by the constructor, that + * parent monitor is configured with a new status containing this warning. + * Otherwise the warning is logged. + * <br> + * Note: Sub-classes may override this method. + */ + @ConfinedToDsfExecutor("fExecutor") + protected void handleWarning() { + if (fParentRequestMonitor != null) { + fParentRequestMonitor.setStatus(getStatus()); + fParentRequestMonitor.done(); + } + } + + /** + * Default handler for a canceled the completion of a request. If this + * monitor has a parent monitor that was configured by the constructor, that + * parent monitor is notified. Otherwise this method does nothing. + * <br> + * Note: Sub-classes may override this method. + */ + @ConfinedToDsfExecutor("fExecutor") + protected void handleCancel() { + if (fParentRequestMonitor != null) { + if (getStatus().getSeverity() == IStatus.CANCEL && !fParentRequestMonitor.isCanceled()) { + fParentRequestMonitor.setStatus(new Status( + IStatus.ERROR, DsfPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Sub-request " + toString() + " was canceled and not handled.'", null)); //$NON-NLS-1$ //$NON-NLS-2$ + } else { + fParentRequestMonitor.setStatus(getStatus()); + } + fParentRequestMonitor.done(); + } + } + + /** + * Default handler for when the executor supplied in the constructor + * rejects the runnable that is submitted invoke this request monitor. + * This usually happens only when the executor is shutting down. + */ + protected void handleRejectedExecutionException() { + MultiStatus logStatus = new MultiStatus(DsfPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Request for monitor: '" + toString() + "' resulted in a rejected execution exception.", null); //$NON-NLS-1$ //$NON-NLS-2$ + logStatus.merge(getStatus()); + if (fParentRequestMonitor != null) { + fParentRequestMonitor.setStatus(logStatus); + fParentRequestMonitor.done(); + } else { + DsfPlugin.getDefault().getLog().log(logStatus); + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/RequestMonitorWithProgress.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/RequestMonitorWithProgress.java new file mode 100644 index 00000000000..7cdd2719799 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/RequestMonitorWithProgress.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems 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.cdt.dsf.concurrent; + +import java.util.concurrent.Executor; + +import org.eclipse.core.runtime.IProgressMonitor; + +/** + * A request monitor which uses a progress monitor as a parent. When the parent + * progress monitor is canceled, the request monitor will also be canceled, + * although the cancellation listeners will not be called. + * + * @since 1.1 + */ +public class RequestMonitorWithProgress extends RequestMonitor { + + private final IProgressMonitor fProgressMonitor; + + public RequestMonitorWithProgress(Executor executor, IProgressMonitor progressMonitor) { + super(executor, null); + fProgressMonitor = progressMonitor; + } + + public IProgressMonitor getProgressMonitor() { + return fProgressMonitor; + } + + @Override + public synchronized boolean isCanceled() { + return super.isCanceled() || fProgressMonitor.isCanceled(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Sequence.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Sequence.java new file mode 100644 index 00000000000..334a7c69e4e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Sequence.java @@ -0,0 +1,682 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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 + * Nokia - added StepWithProgress. Oct, 2008 + *******************************************************************************/ +package org.eclipse.cdt.dsf.concurrent; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.AbstractQueuedSynchronizer; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitor.ICanceledListener; +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubProgressMonitor; + +/** + * Convenience class for implementing a series of commands that need to be + * executed asynchronously. + * <p> + * Certain complex tasks require multiple commands to be executed in a chain, + * because for example result of one command is used as input into another + * command. The typical DSF pattern of solving this problem is the following: + * <li> + * <br> 1. original caller passes a RequestMonitor callback to a method and invokes it + * <br> 2. the method is executed by a subsystem + * <br> 3. when the method is finished it calls another method and passes + * the original callback to it + * <br> 4. steps 2-3 are repeated number of times + * <br> 5. when the last method in a chain is executed, it submits the original + * RequestMonitor callback + * </li> + * <p> + * This pattern is very useful in itself, but it proves very difficult to follow + * because the methods can be scattered accross many classes and systems. Also + * if progress reporting, cancellability, and roll-back ability is required, it + * has to be re-implemented every time. The Sequence class tries to address + * this problem by containing this pattern in a single class. + */ +@ThreadSafe +abstract public class Sequence extends DsfRunnable implements Future<Object> { + + /** + * The abstract class that each step has to implement. + */ + abstract public static class Step { + private Sequence fSequence; + + /** + * Sets the sequence that this step belongs to. It is only accessible + * by the sequence itself, and is not meant to be called by sequence + * sub-classes. + */ + void setSequence(Sequence sequence) { fSequence = sequence; } + + /** Returns the sequence that this step is running in. */ + public Sequence getSequence() { return fSequence; } + + /** + * Executes the step. Overriding classes should perform the + * work in this method. + * @param rm Result token to submit to executor when step is finished. + */ + public void execute(RequestMonitor rm) { + rm.done(); + } + + /** + * Roll back gives the step implementation a chance to undo the + * operation that was performed by execute(). + * <br> + * Note if the {@link #execute(RequestMonitor)} call completes with a + * non-OK status, then rollBack will not be called for that step. + * Instead it will be called for the previous step. + * @param rm Result token to submit to executor when rolling back the step is finished. + */ + public void rollBack(RequestMonitor rm) { + rm.done(); + } + + /** + * Returns the number of progress monitor ticks corresponding to this + * step. + */ + public int getTicks() { return 1; } + + /** + * Task name for this step. This will be displayed in the label of the + * progress monitor of the owner sequence. + * + * @return name of the task carried out by the step, can be + * <code>null</code>, in which case the overall task name will be used. + * + * @since 1.1 + */ + public String getTaskName() { + return ""; //$NON-NLS-1$ + } + } + + /** + * A step that will report execution progress by itself on the progress + * monitor of the owner sequence.<br> + * <br> + * Note we don't offer a rollBack(RequestMonitor, IProgressMonitor) as we + * don't want end user to be able to cancel the rollback. + * + * @since 1.1 + */ + abstract public static class StepWithProgress extends Step { + + @Override + // don't allow subclass to implement this by "final" it. + final public void execute(RequestMonitor rm) { + assert false : "execute(RequestMonitor rm, IProgressMonitor pm) should be called instead"; //$NON-NLS-1$ + } + + /** + * Execute the step with a progress monitor. Note the given progress + * monitor is a sub progress monitor of the owner sequence which is + * supposed to be fully controlled by the step. Namely the step should + * call beginTask() and done() of the monitor. + * + * @param rm + * @param pm + */ + public void execute(RequestMonitor rm, IProgressMonitor pm) { + rm.done(); + pm.done(); + } + } + + /** The synchronization object for this future */ + final Sync fSync = new Sync(); + + /** + * Executor that this sequence is running in. It is used by the sequence + * to submit the runnables for steps, and for submitting the result. + */ + final private DsfExecutor fExecutor; + + /** + * Result callback to invoke when the sequence is finished. Intended to + * be used when the sequence is created and invoked from the executor + * thread. Otherwise, the {@link Future#get()} method is the appropriate + * method of retrieving the result. + */ + final private RequestMonitor fRequestMonitor; + + /** Status indicating the success/failure of the test. Used internally only. */ + @ConfinedToDsfExecutor("getExecutor") + private IStatus fStatus = Status.OK_STATUS; + + @ConfinedToDsfExecutor("getExecutor") + private int fCurrentStepIdx = 0; + + /** Task name for this sequence used with the progress monitor */ + final private String fTaskName; + + /** Task name used when the sequence is being rolled back. */ + final private String fRollbackTaskName; + + final private IProgressMonitor fProgressMonitor; + + /** Convenience constructor with limited arguments. */ + public Sequence(DsfExecutor executor) { + this(executor, new NullProgressMonitor(), "", "", null); //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Creates a sequence with a request monitor. If the client cancels the + * request monitor, then the request monitors in the + * {@link Step#execute(RequestMonitor)} + * implementations will immediately call the cancel listeners to notify. + * + * @param executor The DSF executor which will be used to invoke all + * steps. + * @param rm The request monitor which will be invoked when the sequence + * is completed. + */ + public Sequence(DsfExecutor executor, RequestMonitor rm) { + this(executor, new NullProgressMonitor(), "", "", rm); //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Creates a sequence with a progress monitor. If the progress monitor is + * canceled, then request monitors in the + * {@link Step#execute(RequestMonitor)} implementations will need to call + * rm.isCanceled() to discover the cancellation. + * @param executor The DSF executor which will be used to invoke all + * steps. + * @param pm Progress monitor for monitoring this sequence. + * @param taskName Name that will be used in call to + * {@link IProgressMonitor#beginTask(String, int)},when the task is + * started. + * @param rollbackTaskName Name that will be used in call to + * {@link IProgressMonitor#subTask(String)} if the task is canceled or + * aborted. + * + * @since 1.1 + */ + public Sequence(DsfExecutor executor, IProgressMonitor pm, String taskName, String rollbackTaskName) { + this(executor, pm, taskName, rollbackTaskName, new RequestMonitorWithProgress(ImmediateExecutor.getInstance(), pm)); + } + + /** + * Creates a sequence with a request monitor that includes a progress + * monitor. + * @param executor The DSF executor which will be used to invoke all + * steps. + * @param rm The request monitor containing the progress monitor + * @param taskName Name that will be used in call to + * {@link IProgressMonitor#beginTask(String, int)},when the task is + * started. + * @param rollbackTaskName Name that will be used in call to + * {@link IProgressMonitor#subTask(String)} if the task is canceled or + * aborted. + * + * @since 1.1 + */ + public Sequence(DsfExecutor executor, RequestMonitorWithProgress rm, String taskName, String rollbackTaskName) { + this(executor, rm.getProgressMonitor(), taskName, rollbackTaskName, rm); + } + + /** + * Constructor that initialized the steps and the result callback. + * @param executor The DSF executor which will be used to invoke all + * steps. + * @param pm Progress monitor for monitoring this sequence. This + * parameter cannot be null. + * @param taskName Name that will be used in call to + * {@link IProgressMonitor#beginTask(String, int)},when the task is + * started. + * @param rollbackTaskName Name that will be used in call to + * {@link IProgressMonitor#subTask(String)} if the task is canceled or + * aborted. + * @param Result that will be submitted to executor when sequence is + * finished. Can be null if calling from non-executor thread and using + * {@link Future#get()} method to wait for the sequence result. + * + * @deprecated This constructor should not be used because it creates a + * potential ambiguity when one of the two monitors is canceled. + */ + @Deprecated + public Sequence(DsfExecutor executor, IProgressMonitor pm, String taskName, String rollbackTaskName, RequestMonitor rm) { + fExecutor = executor; + fProgressMonitor = pm; + fTaskName = taskName; + fRollbackTaskName = rollbackTaskName; + fRequestMonitor = rm; + + if (fRequestMonitor != null) { + fRequestMonitor.addCancelListener(new ICanceledListener() { + public void requestCanceled(RequestMonitor rm) { + fSync.doCancel(); + } + }); + } + } + + /** + * Returns the steps to be executed. It is up to the deriving class to + * supply the steps and to ensure that the list of steps will not be + * modified after the sequence is constructed. + * <p> + * Steps are purposely not accepted as part of the DsfConstructor, in + * order to allow deriving classes to create the steps as a field. And a + * setSteps() method is not provided, to guarantee that the steps will not + * be modified once set (perhaps this is a bit paranoid, but oh well). + */ + abstract public Step[] getSteps(); + + + /** Returns the DSF executor for this sequence */ + public DsfExecutor getExecutor() { return fExecutor; } + + /** + * Returns the RequestMonitor callback that is registered with the Sequence + */ + public RequestMonitor getRequestMonitor() { return fRequestMonitor; } + + /** + * The get method blocks until sequence is complete, but always returns null. + * @see java.concurrent.Future#get + */ + public Object get() throws InterruptedException, ExecutionException { + fSync.doGet(); + return null; + } + + /** + * The get method blocks until sequence is complete or until timeout is + * reached, but always returns null. + * @see java.concurrent.Future#get + */ + public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + fSync.doGet(); + return null; + } + + /** + * Don't try to interrupt the DSF executor thread, just ignore the request + * if set. + * <p>If a request monitor was specified when creating a sequence, that + * request monitor will be canceled by this method as well. The client + * can also use the request monitor's cancel method to cancel the sequence. + * + * @see RequestMonitor#cancel() + */ + public boolean cancel(boolean mayInterruptIfRunning) { + // Cancel the request monitor first, to avoid a situation where + // the request monitor is not canceled but the status is set + // to canceled. + if (fRequestMonitor != null) { + fRequestMonitor.cancel(); + } + return fSync.doCancel(); + } + + public boolean isCancelled() { return fSync.doIsCancelled(); } + + public boolean isDone() { return fSync.doIsDone(); } + + + public void run() { + // Change the state to running. + if (fSync.doRun()) { + // Set the reference to this sequence in each step. + int totalTicks = 0; + for (Step step : getSteps()) { + step.setSequence(this); + totalTicks += step.getTicks(); + } + + // Set the task name + if (fTaskName != null) { + fProgressMonitor.beginTask(fTaskName, totalTicks); + } + + // Call the first step + executeStep(0); + } else { + fSync.doFinish(); + } + } + + /** + * To be called only by the step implementation, Tells the sequence to + * submit the next step. + */ + private void executeStep(int nextStepIndex) { + /* + * At end of each step check progress monitor to see if it's cancelled. + * If progress monitor is cancelled, mark the whole sequence as + * cancelled. + */ + if (fProgressMonitor.isCanceled()) { + cancel(false); + } + + /* + * If sequence was cencelled during last step (or before the sequence + * was ever executed), start rolling back the execution. + */ + if (isCancelled()) { + cancelExecution(); + return; + } + + /* + * Check if we've reached the last step. Note that if execution was + * cancelled during the last step (and thus the sequence is + * technically finished, since it was cancelled it will be rolled + * back. + */ + if (nextStepIndex >= getSteps().length) { + finish(); + return; + } + + // Proceed with executing next step. + fCurrentStepIdx = nextStepIndex; + try { + Step currentStep = getSteps()[fCurrentStepIdx]; + final boolean stepControlsProgress = (currentStep instanceof StepWithProgress); + + RequestMonitor rm = new RequestMonitor(fExecutor, fRequestMonitor) { + final private int fStepIdx = fCurrentStepIdx; + + @Override + public void handleSuccess() { + // Check if we're still the correct step. + assert fStepIdx == fCurrentStepIdx; + if (!stepControlsProgress) { + // then sequence handles the progress report. + fProgressMonitor.worked(getSteps()[fStepIdx].getTicks()); + } + executeStep(fStepIdx + 1); + } + + @Override + protected void handleCancel() { + Sequence.this.cancel(false); + cancelExecution(); + }; + + @Override + protected void handleErrorOrWarning() { + abortExecution(getStatus()); + }; + + @Override + public String toString() { + return "Sequence \"" + fTaskName + "\", result for executing step #" + fStepIdx + " = " + getStatus(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + }; + + fProgressMonitor.subTask(currentStep.getTaskName()); + + if (stepControlsProgress) { + + // Create a sub-monitor that will be controlled by the step. + SubProgressMonitor subMon = new SubProgressMonitor(fProgressMonitor, currentStep.getTicks(), + SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK); + + ((StepWithProgress) currentStep).execute(rm, subMon); + } else { // regular Step + currentStep.execute(rm); + } + + } catch (Throwable t) { + /* + * Catching the exception here will only work if the exception + * happens within the execute method. It will not work in cases + * when the execute submits other runnables, and the other runnables + * encounter the exception. + */ + abortExecution(new Status( + IStatus.ERROR, DsfPlugin.PLUGIN_ID, 0, + "Unhandled exception when executing Sequence " + this + ", step #" + fCurrentStepIdx, //$NON-NLS-1$ //$NON-NLS-2$ + t)); + + /* + * Since we caught the exception, it will not be logged by + * DefaultDsfExecutable.afterExecution(). So log it here. + */ + DefaultDsfExecutor.logException(t); + } + } + + /** + * To be called only by the step implementation. Tells the sequence to + * roll back next step. + */ + private void rollBackStep(int stepIdx) { + // If we reach before step 0, finish roll back. + if (stepIdx < 0) { + finish(); + return; + } + + // Proceed with rolling back given step. + fCurrentStepIdx = stepIdx; + try { + getSteps()[fCurrentStepIdx].rollBack(new RequestMonitor(fExecutor, null) { + final private int fStepIdx = fCurrentStepIdx; + @Override + public void handleCompleted() { + // Check if we're still the correct step. + assert fStepIdx == fCurrentStepIdx; + + // Proceed to the next step. + if (isSuccess()) { + // NOTE: The getTicks() is ticks for executing the step, + // not for rollBack, + // though it does not really hurt to use it here. + fProgressMonitor.worked(getSteps()[fStepIdx].getTicks()); + + rollBackStep(fStepIdx - 1); + } else { + abortRollBack(getStatus()); + } + } + @Override + public String toString() { + return "Sequence \"" + fTaskName + "\", result for rolling back step #" + fStepIdx + " = " + getStatus(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + }); + } catch(Throwable t) { + /* + * Catching the exception here will only work if the exception + * happens within the execute method. It will not work in cases + * when the execute submits other runnables, and the other runnables + * encounter the exception. + */ + abortRollBack(new Status( + IStatus.ERROR, DsfPlugin.PLUGIN_ID, 0, + "Unhandled exception when rolling back Sequence " + this + ", step #" + fCurrentStepIdx, //$NON-NLS-1$ //$NON-NLS-2$ + t)); + + /* + * Since we caught the exception, it will not be logged by + * DefaultDsfExecutable.afterExecution(). So log it here. + */ + DefaultDsfExecutor.logException(t); + } + } + + /** + * Tells the sequence that its execution is to be aborted and it + * should start rolling back the sequence as if it was cancelled by user. + */ + private void cancelExecution() { + if (fRollbackTaskName != null) { + fProgressMonitor.subTask(fRollbackTaskName); + } + fStatus = new Status(IStatus.CANCEL, DsfPlugin.PLUGIN_ID, -1, "Sequence \"" + fTaskName + "\" cancelled.", null); //$NON-NLS-1$ //$NON-NLS-2$ + if (fRequestMonitor != null) { + fRequestMonitor.setStatus(fStatus); + } + + /* + * No need to call fSync, it should have been taken care of by + * Future#cancel method. + * + * Note that we're rolling back starting with the current step, + * because the current step was fully executed. This is unlike + * abortExecution() where the current step caused the roll-back. + */ + rollBackStep(fCurrentStepIdx); + } + + /** + * Tells the sequence that its execution is to be aborted and it + * should start rolling back the sequence as if it was cancelled by user. + */ + private void abortExecution(final IStatus error) { + if (fRollbackTaskName != null) { + fProgressMonitor.subTask(fRollbackTaskName); + } + fStatus = error; + if (fRequestMonitor != null) { + fRequestMonitor.setStatus(error); + } + fSync.doAbort(new CoreException(error)); + + // Roll back starting with previous step, since current step failed. + rollBackStep(fCurrentStepIdx - 1); + } + + /** + * Tells the sequence that that is rolling back, to abort roll back, and + * notify the clients. + */ + private void abortRollBack(final IStatus error) { + if (fRollbackTaskName != null) { + fProgressMonitor.subTask(fRollbackTaskName); + } + + /* + * Compose new status based on previous status information and new + * error information. + */ + MultiStatus newStatus = + new MultiStatus(DsfPlugin.PLUGIN_ID, error.getCode(), + "Sequence \"" + fTaskName + "\" failed while rolling back.", null); //$NON-NLS-1$ //$NON-NLS-2$ + newStatus.merge(error); + newStatus.merge(fStatus); + fStatus = newStatus; + + if (fRequestMonitor != null) { + fRequestMonitor.setStatus(newStatus); + } + + finish(); + } + + private void finish() { + if (fRequestMonitor != null) fRequestMonitor.done(); + fSync.doFinish(); + } + + @SuppressWarnings("serial") + final class Sync extends AbstractQueuedSynchronizer { + private static final int STATE_RUNNING = 1; + private static final int STATE_FINISHED = 2; + private static final int STATE_ABORTING = 4; + private static final int STATE_ABORTED = 8; + private static final int STATE_CANCELLING = 16; + private static final int STATE_CANCELLED = 32; + + private Throwable fException; + + private boolean isFinished(int state) { + return (state & (STATE_FINISHED | STATE_CANCELLED | STATE_ABORTED)) != 0; + } + + @Override + protected int tryAcquireShared(int ignore) { + return doIsDone()? 1 : -1; + } + + @Override + protected boolean tryReleaseShared(int ignore) { + return true; + } + + boolean doIsCancelled() { + int state = getState(); + return (state & (STATE_CANCELLING | STATE_CANCELLED)) != 0; + } + + boolean doIsDone() { + return isFinished(getState()); + } + + void doGet() throws InterruptedException, ExecutionException { + acquireSharedInterruptibly(0); + if (getState() == STATE_CANCELLED) throw new CancellationException(); + if (fException != null) throw new ExecutionException(fException); + } + + void doGet(long nanosTimeout) throws InterruptedException, ExecutionException, TimeoutException { + if (!tryAcquireSharedNanos(0, nanosTimeout)) throw new TimeoutException(); + if (getState() == STATE_CANCELLED) throw new CancellationException(); + if (fException != null) throw new ExecutionException(fException); + } + + void doAbort(Throwable t) { + while(true) { + int s = getState(); + if (isFinished(s)) return; + if (compareAndSetState(s, STATE_ABORTING)) break; + } + fException = t; + } + + boolean doCancel() { + while(true) { + int s = getState(); + if (isFinished(s)) return false; + if (s == STATE_ABORTING) return false; + if (compareAndSetState(s, STATE_CANCELLING)) break; + } + return true; + } + + void doFinish() { + while(true) { + int s = getState(); + if (isFinished(s)) return; + if (s == STATE_ABORTING) { + if (compareAndSetState(s, STATE_ABORTED)) break; + } else if (s == STATE_CANCELLING) { + if (compareAndSetState(s, STATE_CANCELLED)) break; + } else { + if (compareAndSetState(s, STATE_FINISHED)) break; + } + } + releaseShared(0); + } + + boolean doRun() { + return compareAndSetState(0, STATE_RUNNING); + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/StackTraceWrapper.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/StackTraceWrapper.java new file mode 100644 index 00000000000..b48d69afa7b --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/StackTraceWrapper.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 Wind River Systems 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.cdt.dsf.concurrent; + +/** + * Untility class for easy pretty-printing stack traces. Local to the + * concurrent package. + */ +@Immutable +class StackTraceWrapper { + final StackTraceElement[] fStackTraceElements; + + StackTraceWrapper(StackTraceElement[] elements) { fStackTraceElements = elements; } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(fStackTraceElements.length * 30); + for (int i = 0; i < fStackTraceElements.length && i < 10; i++) { + builder.append(fStackTraceElements[i]); + if (i < fStackTraceElements.length && i < 10) builder.append("\n at "); //$NON-NLS-1$ + } + return builder.toString(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ThreadSafe.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ThreadSafe.java new file mode 100644 index 00000000000..5ebcdec963a --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ThreadSafe.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems 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.cdt.dsf.concurrent; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation idicating that given package, class, method, or field can be + * access safely from any thread. If declared on package or type, a field + * or method could still be declared with an annotation indicating that it's + * not thread-safe. + * <p> + * Note: the runtime retention policy is there to allow automated testing + * and validation code. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.PACKAGE, ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR}) +@Inherited +@Documented +public @interface ThreadSafe { + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ThreadSafeAndProhibitedFromDsfExecutor.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ThreadSafeAndProhibitedFromDsfExecutor.java new file mode 100644 index 00000000000..4f3188b468d --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ThreadSafeAndProhibitedFromDsfExecutor.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems 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.cdt.dsf.concurrent; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation idicating that given package, class, method, can be accessed on + * any thread, except on the dispatch thread of given DsfExecutor. + * <br> This restriction is desirable if it is expected that the implementation + * behavior is to block the calling thread and execute a transaction using an + * executor. In this situation, if the call is made on the executor's dispach + * thread, the execution would dead-lock. + * <br> + * If declared on package or type, a field or method could still be declared + * with an annotation indicating that it's thread-safe. + * <p> + * Note: the runtime retention policy is there to allow automated testing + * and validation code. + * + * @param value The value indicates the method to use to obtain the executor. + * It should be null if it cannot be determined from the given object. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.PACKAGE, ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR}) +@Inherited +@Documented +public @interface ThreadSafeAndProhibitedFromDsfExecutor { + String value(); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/AbstractDMContext.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/AbstractDMContext.java new file mode 100644 index 00000000000..fd96e0d99f3 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/AbstractDMContext.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 Wind River Systems 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.cdt.dsf.datamodel; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.service.IDsfService; +import org.eclipse.core.runtime.PlatformObject; + +/** + * Base implementation of the IDMContext interface. There are two pieces of + * functionality here: <br> + * 1) The {@link #getAdapter(Class)} implementation which retrieves model + * adapters registered with the session. <br> + * 2) Methods to help compare DM Contexts. <br> + * <p> + * Note: The {@link #equals(Object)} and {@link #hashCode()} methods are + * made abstract to force the deriving classes to provide a proper + * implementation. Data Model Context objects are meant to be used as handles, + * therefore a proper equals implementation is critical. + * </p> + * @param <V> Data Model data type that this context is for. + */ +@Immutable +abstract public class AbstractDMContext extends PlatformObject + implements IDMContext +{ + private final String fSessionId; + private final IDMContext[] fParents; + + /** + * Main constructor provides all data needed to implement the IModelContext + * interface. + */ + public AbstractDMContext(String sessionId, IDMContext[] parents) { + fSessionId = sessionId; + fParents = parents; + for (IDMContext parent : parents) { + assert(parent != null); + } + } + + /** Convenience constructor */ + public AbstractDMContext(IDsfService service, IDMContext[] parents) { + this(service.getSession().getId(), parents); + } + + /** + * Should be used by the deriving class to compare the basic context object + * information. + * @param other the other service to compare to + * @return true if contexts are equal + */ + protected boolean baseEquals(Object other) { + if (other == null) return false; + if ( !(other.getClass().equals(getClass()))) return false; + IDMContext otherCtx = (IDMContext)other; + return getSessionId().equals(otherCtx.getSessionId()) && + areParentsEqual(otherCtx.getParents()); + } + + private boolean areParentsEqual(IDMContext[] otherParents) { + if ( !(fParents.length == otherParents.length) ) return false; + for (int i = 0; i < fParents.length; i++) { + if (!fParents[i].equals(otherParents[i])) { + return false; + } + } + return true; + } + + protected int baseHashCode() { + int parentsHash = 0; + for (Object parent : getParents()) { + parentsHash += parent.hashCode(); + } + return getSessionId().hashCode() + parentsHash; + } + + protected String baseToString() { + StringBuffer retVal = new StringBuffer(); + for (IDMContext parent : fParents) { + retVal.append(parent); + } + return retVal.toString(); + } + + public String getSessionId() { return fSessionId; } + public IDMContext[] getParents() { return fParents; } + + /** + * Overrides the standard platform getAdapter to provide session-specific + * adapters. + * <p> + * ModelContext is intended to be used in views, which call the + * contexts.getAdapter() method to retrieve model-specific content and + * label providers. But since many different sessions could be active + * at the same time, each requiring different content providers, the + * standard platform <code>IAdapterManager</code> is not sufficient in + * handling adapters for the model context object. This is because + * <code>IAdapterManager</code> uses only the class of the adaptable to + * select the correct adapter factoru, while for model context, the + * session is equally important. + * @see org.eclipse.runtime.IAdapterManager + */ + @Override + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapterType) { + Object retVal = null; + DsfSession session = DsfSession.getSession(fSessionId); + if (session != null) { + retVal = session.getModelAdapter(adapterType); + } + if (retVal == null) { + retVal = super.getAdapter(adapterType); + } + return retVal; + } + + /** + * Deriving classes must implement proper equals and hashCode operations + * based on context data. + */ + @Override + abstract public boolean equals(Object obj); + + /** + * Deriving classes must implement proper equals and hashCode operations + * based on context data. + */ + @Override + abstract public int hashCode(); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/AbstractDMEvent.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/AbstractDMEvent.java new file mode 100644 index 00000000000..296433e85fd --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/AbstractDMEvent.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 Wind River Systems 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.cdt.dsf.datamodel; + +import org.eclipse.cdt.dsf.concurrent.Immutable; + +/** + * Base implementation of the IDMEvent interface. It only handles the + * required DM-Context reference. + */ +@Immutable +abstract public class AbstractDMEvent<V extends IDMContext> implements IDMEvent<V> { + + private final V fModelContext; + public AbstractDMEvent(V context) { + fModelContext = context; + } + + public V getDMContext() { + return fModelContext; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/CompositeDMContext.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/CompositeDMContext.java new file mode 100644 index 00000000000..8f114e2dd44 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/CompositeDMContext.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems 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.cdt.dsf.datamodel; + +import java.util.Arrays; + +/** + * Generic DM context used to combine several DM Contexts. This object allows + * clients and other services to combine several contexts into one in order to + * pass them as an argument to a method which takes a generic context as an + * argument. + */ +public class CompositeDMContext implements IDMContext { + + public static String INVALID_SESSION_ID = ""; //$NON-NLS-1$ + + /** + * The list of parent contexts that this composite context is made up of. + */ + private final IDMContext[] fParents; + + /** + * Main constructor provides all data needed to implement the IModelContext + * interface. + * @param parents Array of parent contexts that this composite context is + * made up of. It can be an empty array, but it cannot be null. + */ + public CompositeDMContext(IDMContext[] parents) { + fParents = parents; + } + + /** + * Returns the session ID of the first element in the array of parents of this + * context. May return an empty string if the parents array has no elements. + * <p> + * Note: The session ID is primarily used by UI components to get access to the + * correct session and executor for the given context. The composite context is + * intended to be created by clients which already know the session ID so + * the fact that this method may not return a reliable result is acceptable. + * </p> + */ + public String getSessionId() { + IDMContext[] parents = getParents(); + if (parents.length > 0) { + return parents[0].getSessionId(); + } else { + return INVALID_SESSION_ID; + } + } + + /** + * Returns the list of parents that this composite context is based on. Subclasses + * may override this method to calculate their own set of parents. + */ + public IDMContext[] getParents() { + return fParents; + } + + /** + * Returns the given adapter of the last DMVMContext element found in the tree + * path of this composite context. Will return null if no DMVMContext is found + * in path. + * @see #getSessionId() + */ + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapterType) { + IDMContext[] parents = getParents(); + if (parents.length > 0) { + return parents[0].getAdapter(adapterType); + } else { + return null; + } + } + + @Override + public boolean equals(Object obj) { + return obj instanceof CompositeDMContext && Arrays.equals(((CompositeDMContext)obj).getParents(), getParents()); + } + + @Override + public int hashCode() { + return Arrays.hashCode(getParents()); + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/DMContexts.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/DMContexts.java new file mode 100644 index 00000000000..bf0c7102c5c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/DMContexts.java @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 Wind River Systems 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 + * Ericsson - Modified for additional features in DSF Reference implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.datamodel; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; + + +/** + * Holder for utility static methods for manipulating IDMContext objects. + */ +public class DMContexts { + + /** + * Convenience constant. + */ + public static final IDMContext[] EMPTY_CONTEXTS_ARRAY = new IDMContext[0]; + + /** + * Finds a data model context of given type among ancestors of the + * specified context. The returned ancestor is the one closest to the + * specified context, in terms of depth. + * + * Note that for efficiency, this method does not re-use getAllAncestorsOfType() + * to avoid the unnecessary creation of an array. + * + * @param ctx DMC to search. + * @param ancestorType Class type of the desired DMC ancestor. + * @return Returns the ancestor if found, null otherwise. + */ + @ThreadSafe + @SuppressWarnings("unchecked") + public static <V extends IDMContext> V getAncestorOfType(IDMContext ctx, Class<V> ancestorType) { + if(ctx == null) + return null; + // Check the first context here for efficiency + if (ancestorType.isAssignableFrom(ctx.getClass())) { + return (V)ctx; + } + + // Use a LinkedHashSet to avoid duplicates and preserver insertion-order + Set<IDMContext> nodes = new LinkedHashSet<IDMContext>(); + nodes.addAll(Arrays.asList(ctx.getParents())); + while (nodes.isEmpty() == false) { + Set<IDMContext> parents = nodes; + nodes = new LinkedHashSet<IDMContext>(); + for (IDMContext parent : parents) { + if (ancestorType.isAssignableFrom(parent.getClass())) { + return (V)parent; + } + + nodes.addAll(Arrays.asList(parent.getParents())); + } + } + + return null; + } + + /** + * Finds all data model contexts of given type among ancestors of the + * specified context. Ancestors are returned in order of closest to farthest, + * in terms of depth. + * @param ctx DMC to search. + * @param ancestorType Class type of the desired DMC ancestor. + * @return Returns all ancestors found, null if none. + * @since 1.1 + */ + @ThreadSafe + @SuppressWarnings("unchecked") + public static <V extends IDMContext> V[] getAllAncestorsOfType(IDMContext ctx, Class<V> ancestorType) { + if(ctx == null) + return null; + + // Use a LinkedHashSet to avoid duplicates and preserver insertion-order + Set<V> requestedAncestors = new LinkedHashSet<V>(); + Set<IDMContext> nodes = new LinkedHashSet<IDMContext>(); + nodes.add(ctx); + while (nodes.isEmpty() == false) { + Set<IDMContext> parents = nodes; + nodes = new LinkedHashSet<IDMContext>(); + for (IDMContext parent : parents) { + if (ancestorType.isAssignableFrom(parent.getClass())) { + requestedAncestors.add((V)parent); + } + + nodes.addAll(Arrays.asList(parent.getParents())); + } + } + + if (requestedAncestors.isEmpty()) return null; + else { + V[] v = (V[])Array.newInstance(ancestorType, 0); + return requestedAncestors.toArray(v); + } + } + + /** + * Checks all ancestors for a given context to see if the given + * potentialAncestor is in fact an ancestor. + * @param dmc DM Contexts who's ancestors to check. + * @param potentialAncestor Ancestor context to look for. + * @return true if a match is found. + */ + @ThreadSafe + public static boolean isAncestorOf(IDMContext dmc, IDMContext potentialAncestor) { + // Check the direct parents for a match. + for (IDMContext parentDmc : dmc.getParents()) { + if (potentialAncestor.equals(parentDmc)) { + return true; + } + } + + // Recursively check the parents' parents for a match. + for (IDMContext parentDmc : dmc.getParents()) { + if (isAncestorOf(parentDmc, potentialAncestor)) { + return true; + } + } + + // No match. + return false; + } + + /** + * Traverses all the parents of a context and converts the whole + * into a list. + */ + @ThreadSafe + public static List<IDMContext> toList(IDMContext dmc) { + /* + * This method is implemented recursively, which is not necessarily + * the most efficient way to do this. + */ + List<IDMContext> list = new ArrayList<IDMContext>(); + list.add(dmc); + + for (IDMContext parentDmc : dmc.getParents()) { + list.addAll(toList(parentDmc)); + } + return list; + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMContext.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMContext.java new file mode 100644 index 00000000000..2d8292ba17d --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMContext.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.dsf.datamodel; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.core.runtime.IAdaptable; + +/** + * The base class for data model objects. + * <p> + * DSF services need to return objects to clients which can be used as + * handles to track data stored in the service. Clients such as lazy-loading + * tree and table views retrieve a list of handles, then as needed, they + * retrieve the children and label information for these handles. Because of + * this pattern, services need to be able to return a set of handle objects, + * then as needed clients can retrieve data corresponding to these handles. + * The Data Model Context object is the interface that DSF services should use + * to represent the handle objects that are to be referenced by view model. + * <p> + * <i>Note: DM contexts are meant to be immutable and thus accessible from + * any thread instead of just the services dispatch thread. This is because + * clients may need to call context objects' methods on non-dispatch thread, + * especially equals and hashCode.</i> + * <p> + * <i>Note #2: DM Contexts should also avoid holding references to service + * instances or other large chunks of data, because some of the clients may + * hold onto these objects for longer time than the life of the service. + * This may prevent the service from being garbage collected, possibly keeping + * a lot of resources tied up. + * + */ +@Immutable +public interface IDMContext extends IAdaptable +{ + /** + * Each model context object needs to track the session from which it + * originated. The session ID allows clients to choose the correct + * dispatch thread with which to access the service, and it allows the + * service to be uniquely identified among other sessions. + * @return Session ID of the service that originated the context. + */ + public String getSessionId(); + + /** + * Returns the parent context of this context. ModelContext objects can be + * chained this way to allow methods that require context from multiple + * services to retrieve this context from a single handle that comes from + * the client. + * @return parent context of this context. + */ + public IDMContext[] getParents(); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMData.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMData.java new file mode 100644 index 00000000000..045f8c92013 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMData.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 Wind River Systems 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.cdt.dsf.datamodel; + +import org.eclipse.cdt.dsf.concurrent.Immutable; + +/** + * Marker interface for data corresponding to IDMContext, retrieved from a + * service. These data objects are meant to be processed by clients on + * different threads, therefore they should be immutable. + */ +@Immutable +public interface IDMData { +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMEvent.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMEvent.java new file mode 100644 index 00000000000..40edf672e51 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMEvent.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 Wind River Systems 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.cdt.dsf.datamodel; + +/** + * Common interface for events that signify changes in the data model. + * The sub-classes should contain specific information about the event, while + * this base class only identifies the DM Context that is affected. + * @param <V> Data Model context type that is affected by this event. + */ +public interface IDMEvent <V extends IDMContext> { + V getDMContext(); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMService.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMService.java new file mode 100644 index 00000000000..f0071711a62 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/IDMService.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.dsf.datamodel; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.service.IDsfService; + +/** + * Interface for DSF services that provide model data to clients. + * <p> + * For completeness this service interface derives from <code>IDMData</data> + * and has a method which allows clients to retrieve the DM Context that + * represents the service data. + * + * @deprecated Without getModelData method this service has no function. + * There's also no need for it as a marker interface so we may as well + * get rid of it. + */ +public interface IDMService extends IDsfService { + /** + * Retrieves model data object for given context. This method makes it + * un-necessary for every model service to declare a separate method + * for retrieving model data of specific type. + * + * @param <V> The Data Model Data type that is to be retrieved. + * @param dmc Data Model Context for the data model data object to be retrieved. + * @param rm Request completion monitor to be filled in with the Data Model Data. + * + * @deprecated This method is now deprecated as there is no compile-time linking + * between IDMContext and IDMData objects (see bug 205132) + */ + @Deprecated + void getModelData(IDMContext dmc, DataRequestMonitor<?> rm); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/package.html b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/package.html new file mode 100644 index 00000000000..2ea664a1ee4 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/datamodel/package.html @@ -0,0 +1,26 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> + <meta content="text/html; charset=ISO-8859-1" + http-equiv="content-type"> + <title>Eclipse Device Debug - Debugger Services Framework - Data Model</title> +</head> +<body> +Provides a base API and utilities for expoding data model through DSF +services.<br> +<br> +<h2>Package Specification</h2> +Practically speaking, all state data held by the DSF services makes up +the "data mode" of the service session. However, to make it easy +to present this data in standard debug views, as well as customizable +views, it is useful to present the data using a consisten pattern and +with a set of published APIs and utilities. This package aims to +provide these APIs and utilities.<br> +<h3>Development Plans</h3> +This package is a work in progress and it is missing a major +feature. This feature is being able to automatically parametrize +the contents of the data model in order to generically traverse it, and +to write data-driven framework for populating views with model data.<br> +<br> +</body> +</html> diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/internal/provisional/model/IMemoryBlockUpdatePolicyProvider.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/internal/provisional/model/IMemoryBlockUpdatePolicyProvider.java new file mode 100644 index 00000000000..7f57a6c812c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/internal/provisional/model/IMemoryBlockUpdatePolicyProvider.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems 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 - Ted Williams - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.internal.provisional.model; + +/* + * This interface is EXPERIMENTAL. +*/ + +public interface IMemoryBlockUpdatePolicyProvider +{ + public String[] getUpdatePolicies(); + + public String getUpdatePolicyDescription(String id); + + public String getUpdatePolicy(); + + public void setUpdatePolicy(String id); + + public void clearCache(); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/model/DsfMemoryBlock.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/model/DsfMemoryBlock.java new file mode 100644 index 00000000000..88c8a119eb1 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/model/DsfMemoryBlock.java @@ -0,0 +1,634 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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 + * Ericsson Communication - upgrade IF to IMemoryBlockExtension + * Ericsson Communication - added support for 64 bit processors + * Ericsson Communication - added support for changed bytes + * Ericsson Communication - better management of exceptions + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.model; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.debug.internal.provisional.model.IMemoryBlockUpdatePolicyProvider; +import org.eclipse.cdt.dsf.debug.service.IMemory; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryChangedEvent; +import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason; +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.utils.Addr64; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.PlatformObject; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugEvent; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.model.IDebugTarget; +import org.eclipse.debug.core.model.IMemoryBlockExtension; +import org.eclipse.debug.core.model.IMemoryBlockRetrieval; +import org.eclipse.debug.core.model.MemoryByte; + +/** + * This class manages the memory block retrieved from the target as a result + * of a getBytesFromAddress() call from the platform. + * + * It performs its read/write functions using the MemoryService. + */ +public class DsfMemoryBlock extends PlatformObject implements IMemoryBlockExtension, IMemoryBlockUpdatePolicyProvider +{ + private final static String UPDATE_POLICY_AUTOMATIC = "Automatic"; //$NON-NLS-1$ + private final static String UPDATE_POLICY_MANUAL = "Manual"; //$NON-NLS-1$ + private final static String UPDATE_POLICY_BREAKPOINT = "On Breakpoint"; //$NON-NLS-1$ + + private final IMemoryDMContext fContext; + private final ILaunch fLaunch; + private final IDebugTarget fDebugTarget; + private final DsfMemoryBlockRetrieval fRetrieval; + private final String fModelId; + private final String fExpression; + private final BigInteger fBaseAddress; + + private BigInteger fBlockAddress; + private int fLength; + private int fWordSize; + private MemoryByte[] fBlock; + + private String fUpdatePolicy = UPDATE_POLICY_AUTOMATIC; + + private ArrayList<Object> fConnections = new ArrayList<Object>(); + + @SuppressWarnings("unused") + private boolean isEnabled; + + /** + * Constructor. + * + * @param retrieval - the MemoryBlockRetrieval (session context) + * @param modelId - + * @param expression - the displayed expression in the UI + * @param address - the actual memory block start address + * @param word_size - the number of bytes per address + * @param length - the requested block length (could be 0) + */ + DsfMemoryBlock(DsfMemoryBlockRetrieval retrieval, IMemoryDMContext context, String modelId, String expression, BigInteger address, int word_size, long length) { + fLaunch = retrieval.getLaunch(); + fDebugTarget = retrieval.getDebugTarget(); + fRetrieval = retrieval; + fContext = context; + fModelId = modelId; + fExpression = expression; + fBaseAddress = address; + + // Current block information + fBlockAddress = address; + fWordSize = word_size; + fLength = (int) length; + fBlock = null; + + try { + fRetrieval.getExecutor().execute(new Runnable() { + public void run() { + fRetrieval.getSession().addServiceEventListener(DsfMemoryBlock.this, null); + } + }); + } catch (RejectedExecutionException e) { + // Session is shut down. + } + } + + // //////////////////////////////////////////////////////////////////////// + // IAdaptable + // //////////////////////////////////////////////////////////////////////// + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.PlatformObject#getAdapter(java.lang.Class) + */ + @SuppressWarnings("unchecked") + @Override + public Object getAdapter(Class adapter) { + if (adapter.isAssignableFrom(DsfMemoryBlockRetrieval.class)) { + return fRetrieval; + } + return super.getAdapter(adapter); + } + + // //////////////////////////////////////////////////////////////////////// + // IDebugElement + // //////////////////////////////////////////////////////////////////////// + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IDebugElement#getDebugTarget() + */ + public IDebugTarget getDebugTarget() { + return fDebugTarget; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IDebugElement#getModelIdentifier() + */ + public String getModelIdentifier() { + return fModelId; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IDebugElement#getLaunch() + */ + public ILaunch getLaunch() { + return fLaunch; + } + + // //////////////////////////////////////////////////////////////////////// + // IMemoryBock interface - obsoleted by IMemoryBlockExtension + // //////////////////////////////////////////////////////////////////////// + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlock#getStartAddress() + */ + public long getStartAddress() { + // Not implemented (obsolete) + return 0; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlock#getLength() + */ + public long getLength() { + // Not implemented (obsolete) + return 0; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlock#getBytes() + */ + public byte[] getBytes() throws DebugException { + // Not implemented (obsolete) + return new byte[0]; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlock#supportsValueModification() + */ + public boolean supportsValueModification() { + return fRetrieval.supportsValueModification(); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlock#setValue(long, byte[]) + */ + public void setValue(long offset, byte[] bytes) throws DebugException { + // Not implemented (obsolete) + } + + // //////////////////////////////////////////////////////////////////////// + // IMemoryBlockExtension interface + // //////////////////////////////////////////////////////////////////////// + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#getExpression() + */ + public String getExpression() { + return fExpression; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#getBigBaseAddress() + */ + public BigInteger getBigBaseAddress() throws DebugException { + return fBaseAddress; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#getMemoryBlockStartAddress() + */ + public BigInteger getMemoryBlockStartAddress() throws DebugException { + // Null indicates that memory can be retrieved at addresses lower than the block base address + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#getMemoryBlockEndAddress() + */ + public BigInteger getMemoryBlockEndAddress() throws DebugException { + // Null indicates that memory can be retrieved at addresses higher the block base address + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#getBigLength() + */ + public BigInteger getBigLength() throws DebugException { + // -1 indicates that memory block is unbounded + return BigInteger.valueOf(-1); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#getAddressSize() + */ + public int getAddressSize() throws DebugException { + return fRetrieval.getAddressSize(); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#supportBaseAddressModification() + */ + public boolean supportBaseAddressModification() throws DebugException { + return fRetrieval.supportBaseAddressModification(); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#supportsChangeManagement() + */ + public boolean supportsChangeManagement() { + return true; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#setBaseAddress(java.math.BigInteger) + */ + public void setBaseAddress(BigInteger address) throws DebugException { + fBlockAddress = address; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#getBytesFromOffset(java.math.BigInteger, long) + */ + public MemoryByte[] getBytesFromOffset(BigInteger offset, long units) throws DebugException { + return getBytesFromAddress(fBlockAddress.add(offset), units); + } + + private boolean fUseCachedData = false; + + public void clearCache() { + fUseCachedData = false; + } + + @DsfServiceEventHandler + public void handleCacheSuspendEvent(IRunControl.ISuspendedDMEvent e) { + if (e.getReason() == StateChangeReason.BREAKPOINT) + fUseCachedData = false; + } + + private boolean isUseCacheData() + { + if (fUpdatePolicy.equals(DsfMemoryBlock.UPDATE_POLICY_BREAKPOINT)) + return fUseCachedData; + + if (fUpdatePolicy.equals(DsfMemoryBlock.UPDATE_POLICY_MANUAL)) + return fUseCachedData; + + return false; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#getBytesFromAddress(java.math.BigInteger, long) + */ + public MemoryByte[] getBytesFromAddress(BigInteger address, long units) throws DebugException { + + if (isUseCacheData() && fBlockAddress.compareTo(address) == 0 && units * getAddressableSize() <= fBlock.length) + return fBlock; + + MemoryByte[] newBlock = fetchMemoryBlock(address, units); + int newLength = (newBlock != null) ? newBlock.length : 0; + + // If the retrieved block overlaps with the cached block, flag the changed bytes + // so they can be properly highlighted in the platform Memory view. + // Note: In the standard Memory view, the values are displayed in cells of 4 bytes + // (on 4-bytes address boundaries) and all 4 bytes have to be flagged so the + // cell is highlighted (and the delta sigh is shown). + if (fBlock != null && newLength > 0) { + switch (fBlockAddress.compareTo(address)) { + // Cached block begins before the retrieved block location + // If there is no overlap, there are no bytes to flag + case -1: + { + // Determine the distance between the cached and the + // requested block addresses + BigInteger bigDistance = address.subtract(fBlockAddress); + + // If the distance does not exceed the length of the cached block, + // then there is some overlap between the blocks and we have to + // mark the changed bytes (if applicable) + if (bigDistance.compareTo(BigInteger.valueOf(fLength)) == -1) { + // Here we can reasonably assume that java integers are OK + int distance = bigDistance.intValue(); + int length = fLength - distance; + + // Work by cells of 4 bytes + for (int i = 0; i < length; i += 4) { + // Determine if any byte within the cell was modified + boolean changed = false; + for (int j = i; j < (i + 4) && j < length; j++) { + newBlock[j].setFlags(fBlock[distance + j].getFlags()); + if (newBlock[j].getValue() != fBlock[distance + j].getValue()) + changed = true; + } + // If so, flag the whole cell as modified + if (changed) + for (int j = i; j < (i + 4) && j < length; j++) { + newBlock[j].setHistoryKnown(true); + newBlock[j].setChanged(true); + } + } + + } + break; + } + + // Cached block begins before the retrieved block location + // If there is no overlap, there are no bytes to flag + // (this block of code is symmetric with the previous one) + case 0: + case 1: + { + // Determine the distance between the cached and the + // requested block addresses + BigInteger bigDistance = fBlockAddress.subtract(address); + + // If the distance does not exceed the length of the new block, + // then there is some overlap between the blocks and we have to + // mark the changed bytes (if applicable) + if (bigDistance.compareTo(BigInteger.valueOf(newLength)) == -1) { + // Here we can reasonably assume that java integers are OK + int distance = bigDistance.intValue(); + int length = newLength - distance; + + // Work by cells of 4 bytes + for (int i = 0; i < length; i += 4) { + // Determine if any byte within the cell was modified + boolean changed = false; + for (int j = i; j < (i + 4) && j < length; j++) { + newBlock[distance + j].setFlags(fBlock[j].getFlags()); + if (newBlock[distance + j].getValue() != fBlock[j].getValue()) + changed = true; + } + // If so, flag the whole cell as modified + if (changed) + for (int j = i; j < (i + 4) && j < length; j++) { + newBlock[distance + j].setHistoryKnown(true); + newBlock[distance + j].setChanged(true); + } + } + } + break; + } + + default: + break; + } + } + + // Update the internal state + fBlock = newBlock; + fBlockAddress = address; + fLength = newLength; + + if (fUpdatePolicy.equals(DsfMemoryBlock.UPDATE_POLICY_BREAKPOINT)) + fUseCachedData = true; + else if (fUpdatePolicy.equals(DsfMemoryBlock.UPDATE_POLICY_MANUAL)) + fUseCachedData = true; + + return fBlock; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#setValue(java.math.BigInteger, byte[]) + */ + public void setValue(BigInteger offset, byte[] bytes) throws DebugException { + writeMemoryBlock(offset.longValue(), bytes); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#connect(java.lang.Object) + */ + public void connect(Object client) { + if (!fConnections.contains(client)) + fConnections.add(client); + if (fConnections.size() == 1) + enable(); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#disconnect(java.lang.Object) + */ + public void disconnect(Object client) { + if (fConnections.contains(client)) + fConnections.remove(client); + if (fConnections.size() == 0) + disable(); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#getConnections() + */ + public Object[] getConnections() { + return fConnections.toArray(); + } + + private void enable() { + isEnabled = true; + } + + private void disable() { + isEnabled = false; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#dispose() + */ + public void dispose() throws DebugException { + try { + fRetrieval.getExecutor().execute(new Runnable() { + public void run() { + fRetrieval.getSession().removeServiceEventListener(DsfMemoryBlock.this); + } + }); + } catch (RejectedExecutionException e) { + // Session is down. + } + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#getMemoryBlockRetrieval() + */ + public IMemoryBlockRetrieval getMemoryBlockRetrieval() { + return fRetrieval; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IMemoryBlockExtension#getAddressableSize() + */ + public int getAddressableSize() throws DebugException { + return fRetrieval.getAddressableSize(); + } + + /////////////////////////////////////////////////////////////////////////// + // Helper functions + /////////////////////////////////////////////////////////////////////////// + + /* + * The real thing. Since the original call is synchronous (from a platform + * Job), we use a Query that will patiently wait for the underlying + * asynchronous calls to complete before returning. + * + * @param bigAddress + * @param length + * @return MemoryByte[] + * @throws DebugException + */ + private MemoryByte[] fetchMemoryBlock(BigInteger bigAddress, final long length) throws DebugException { + + // For the IAddress interface + final Addr64 address = new Addr64(bigAddress); + + // Use a Query to synchronize the downstream calls + Query<MemoryByte[]> query = new Query<MemoryByte[]>() { + @Override + protected void execute(final DataRequestMonitor<MemoryByte[]> drm) { + IMemory memoryService = (IMemory) fRetrieval.getServiceTracker().getService(); + if (memoryService != null) { + // Go for it + memoryService.getMemory( + fContext, address, 0, fWordSize, (int) length, + new DataRequestMonitor<MemoryByte[]>(fRetrieval.getExecutor(), drm) { + @Override + protected void handleSuccess() { + drm.setData(getData()); + drm.done(); + } + }); + } + + } + }; + fRetrieval.getExecutor().execute(query); + + try { + return query.get(); + } catch (InterruptedException e) { + throw new DebugException(new Status(IStatus.ERROR, + DsfPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, + "Error reading memory block (InterruptedException)", e)); //$NON-NLS-1$ + } catch (ExecutionException e) { + throw new DebugException(new Status(IStatus.ERROR, + DsfPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, + "Error reading memory block (ExecutionException)", e)); //$NON-NLS-1$ + } + } + + /* Writes an array of bytes to memory. + * + * @param offset + * @param bytes + * @throws DebugException + */ + private void writeMemoryBlock(final long offset, final byte[] bytes) throws DebugException { + + // For the IAddress interface + final Addr64 address = new Addr64(fBaseAddress); + + // Use a Query to synchronize the downstream calls + Query<MemoryByte[]> query = new Query<MemoryByte[]>() { + @Override + protected void execute(final DataRequestMonitor<MemoryByte[]> drm) { + IMemory memoryService = (IMemory) fRetrieval.getServiceTracker().getService(); + if (memoryService != null) { + // Go for it + memoryService.setMemory( + fContext, address, offset, fWordSize, bytes.length, bytes, + new RequestMonitor(fRetrieval.getExecutor(), null)); + } + + } + }; + fRetrieval.getExecutor().execute(query); + + try { + query.get(); + } catch (InterruptedException e) { + throw new DebugException(new Status(IStatus.ERROR, + DsfPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, + "Error writing memory block (InterruptedException)", e)); //$NON-NLS-1$ + } catch (ExecutionException e) { + throw new DebugException(new Status(IStatus.ERROR, + DsfPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, + "Error writing memory block (ExecutionException)", e)); //$NON-NLS-1$ + } + } + + /////////////////////////////////////////////////////////////////////////// + // Event Handlers + /////////////////////////////////////////////////////////////////////////// + + @DsfServiceEventHandler + public void eventDispatched(IRunControl.ISuspendedDMEvent e) { + + // Clear the "Changed" flags after each run/resume/step + for (int i = 0; i < fLength; i++) + fBlock[i].setChanged(false); + + // Generate the MemoryChangedEvents + handleMemoryChange(BigInteger.ZERO); + } + + @DsfServiceEventHandler + public void eventDispatched(IMemoryChangedEvent e) { + + // Check if we are in the same address space + if (e.getDMContext().equals(fContext)) { + IAddress[] addresses = e.getAddresses(); + for (int i = 0; i < addresses.length; i++) + handleMemoryChange(addresses[i].getValue()); + } + } + + /** + * @param address + * @param length + */ + public void handleMemoryChange(BigInteger address) { + + // Check if the change affects this particular block (0 is universal) + BigInteger fEndAddress = fBlockAddress.add(BigInteger.valueOf(fLength)); + if (address.equals(BigInteger.ZERO) || + ((fBlockAddress.compareTo(address) != 1) && (fEndAddress.compareTo(address) == 1))) + { + // Notify the event listeners + DebugEvent debugEvent = new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CONTENT); + DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { debugEvent }); + } + } + + public String[] getUpdatePolicies() { + return new String[] {UPDATE_POLICY_AUTOMATIC, UPDATE_POLICY_MANUAL, UPDATE_POLICY_BREAKPOINT}; + } + + public String getUpdatePolicy() + { + return fUpdatePolicy; + } + + public void setUpdatePolicy(String policy) + { + fUpdatePolicy = policy; + } + + public String getUpdatePolicyDescription(String id) { + return id; + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/model/DsfMemoryBlockRetrieval.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/model/DsfMemoryBlockRetrieval.java new file mode 100644 index 00000000000..6c224b78c11 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/model/DsfMemoryBlockRetrieval.java @@ -0,0 +1,505 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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 + * Ericsson Communication - upgrade IF to IMemoryBlockRetrievalExtension + * Ericsson Communication - added Expression evaluation + * Ericsson Communication - added support for 64 bit processors + * Ericsson Communication - added support for event handling + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.model; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.IMemory; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData; +import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext; +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.cdt.dsf.service.DsfServices; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.service.IDsfService; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.PlatformObject; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.debug.core.model.IDebugTarget; +import org.eclipse.debug.core.model.IMemoryBlock; +import org.eclipse.debug.core.model.IMemoryBlockExtension; +import org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension; +import org.osgi.framework.BundleContext; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.util.tracker.ServiceTracker; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * Implementation of memory access API of the Eclipse standard debug model. + * + * The DsfMemoryBlockRetrieval is not an actual memory block but rather a + * reference to the memory address space for an execution context (e.g. a + * process) within a debug session. From this debug 'context', memory blocks + * can then be read/written. + * + * Note: For the reference application, The IMemoryBlockRetrievalExtension + * is implemented. This will result in getExtendedMemoryBlock() being called + * when a memory block is selected from the platform memory pane. + * + * However, if the 'simpler' IMemoryBlockRetrieval is to be implemented, the + * code will still be functional after some trivial adjustments. + * + */ +public class DsfMemoryBlockRetrieval extends PlatformObject implements IMemoryBlockRetrievalExtension +{ + private final String fModelId; + private final DsfSession fSession; + private final DsfExecutor fExecutor; + private final String fContextString; + private final ServiceTracker fMemoryServiceTracker; + private final ServiceTracker fExpressionServiceTracker; + + private final ILaunchConfiguration fLaunchConfig; + private final ILaunch fLaunch; + private final IDebugTarget fDebugTarget; + private final boolean fSupportsValueModification; + private final boolean fSupportBaseAddressModification; + private final int fAddressSize; + private final int fWordSize; // Number of bytes per address + + /** + * Constructor + * + * @param modelId + * @param dmc + * @throws DebugException + */ + public DsfMemoryBlockRetrieval(String modelId, ILaunchConfiguration config, DsfSession session) throws DebugException { + + // DSF stuff + fModelId = modelId; + + // FIXME: (Bug228573) Currently memory contexts are differentiated by + // sessionID so there is no way to guarantee the memory blocks will be + // reinstated in the correct memory space. + // Need a way to create deterministically the context ID from a unique + // target, ideally from the launch configuration (or derived from it). + // For the time being, just put some constant. This will work until we + // support multiple targets in the same launch. + // fContextString = fContext.toString(); + fContextString = "Context string"; //$NON-NLS-1$ + + fSession = session; + if (fSession == null) { + throw new IllegalArgumentException( + "Session " + session + " is not active"); //$NON-NLS-1$ //$NON-NLS-2$ + } + fExecutor = fSession.getExecutor(); + BundleContext bundle = DsfPlugin.getBundleContext(); + + // Here we chose to use 2 distinct service trackers instead of an + // amalgamated one because it is less error prone (and we are lazy). + + // Create a tracker for the MemoryService + String memoryServiceFilter = DsfServices.createServiceFilter(IMemory.class, session.getId()); + + try { + fMemoryServiceTracker = new ServiceTracker( + bundle, bundle.createFilter(memoryServiceFilter), null); + } catch (InvalidSyntaxException e) { + throw new DebugException(new Status(IStatus.ERROR, + DsfPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, + "Error creating service filter.", e)); //$NON-NLS-1$ + } + fMemoryServiceTracker.open(); + + // Create a tracker for the ExpressionService + String expressionServiceFilter = "(&" + //$NON-NLS-1$ + "(OBJECTCLASS=" //$NON-NLS-1$ + + IExpressions.class.getName() + + ")" + //$NON-NLS-1$ + "(" + IDsfService.PROP_SESSION_ID //$NON-NLS-1$ + + "=" + session.getId() + ")" + //$NON-NLS-1$//$NON-NLS-2$ + ")"; //$NON-NLS-1$ + + try { + fExpressionServiceTracker = new ServiceTracker( + bundle, bundle.createFilter(expressionServiceFilter), null); + } catch (InvalidSyntaxException e) { + throw new DebugException(new Status(IStatus.ERROR, + DsfPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, + "Error creating service filter.", e)); //$NON-NLS-1$ + } + fExpressionServiceTracker.open(); + + // Launch configuration information + fLaunchConfig = config; + fLaunch = null; + fDebugTarget = null; + fAddressSize = 4; // Get this from the launch configuration + fWordSize = 1; // Get this from the launch configuration + fSupportsValueModification = true; // Get this from the launch configuration + fSupportBaseAddressModification = false; // Get this from the launch configuration + } + + /////////////////////////////////////////////////////////////////////////// + // Memory monitors persistence + /////////////////////////////////////////////////////////////////////////// + + /* + * In the launch configuration file, the memory block entry is structured + * as follows (note: this differs from CDI): + * + * <stringAttribute + * key="org.eclipse.dsf.launch.MEMORY_BLOCKS" + * value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> + * <memoryBlockExpressionList context=[memory context ID]> + * <memoryBlockExpression label=[monitor label] address=[base address]/> + * <memoryBlockExpression ...> + * ... + * </memoryBlockExpressionList> + * ... + * <memoryBlockExpressionList context=...> + * ... + * </memoryBlockExpressionList>" + * /> + */ + + //------------------------------------------------------------------------- + // Memory blocks memento tags + //------------------------------------------------------------------------- + + // These 2 really belong in the DSF launch configuration class... + private static final String DSF_LAUNCH_ID = "org.eclipse.dsf.launch"; //$NON-NLS-1$ + private static final String ATTR_DEBUGGER_MEMORY_BLOCKS = DSF_LAUNCH_ID + ".MEMORY_BLOCKS"; //$NON-NLS-1$ + + private static final String MEMORY_BLOCK_EXPRESSION_LIST = "memoryBlockExpressionList"; //$NON-NLS-1$ + private static final String ATTR_EXPRESSION_LIST_CONTEXT = "context"; //$NON-NLS-1$ + private static final String MEMORY_BLOCK_EXPRESSION = "memoryBlockExpression"; //$NON-NLS-1$ + private static final String ATTR_MEMORY_BLOCK_EXPR_LABEL = "label"; //$NON-NLS-1$ + private static final String ATTR_MEMORY_BLOCK_EXPR_ADDRESS = "address"; //$NON-NLS-1$ + + //------------------------------------------------------------------------- + // Install persisted memory monitors + //------------------------------------------------------------------------- + + /** + * Restore the memory monitors from the memento in the launch configuration + */ + public void initialize(final IMemoryDMContext memoryCtx) { + try { + final String memento = fLaunchConfig.getAttribute(ATTR_DEBUGGER_MEMORY_BLOCKS, ""); //$NON-NLS-1$ + if (memento != null && memento.trim().length() != 0) { + // Submit the runnable to install the monitors on dispatch thread. + getExecutor().submit(new Runnable() { + public void run() { + try { + createBlocksFromConfiguration(memoryCtx, memento); + } catch (CoreException e) { + DsfPlugin.getDefault().getLog().log(e.getStatus()); + } + } + }); + } + } catch (CoreException e) { + DsfPlugin.getDefault().getLog().log(e.getStatus()); + } + } + + private void createBlocksFromConfiguration(IMemoryDMContext memoryCtx, String memento) throws CoreException { + + // Parse the memento and validate its type + Element root = DebugPlugin.parseDocument(memento); + if (!root.getNodeName().equalsIgnoreCase(MEMORY_BLOCK_EXPRESSION_LIST)) { + IStatus status = new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, DebugPlugin.INTERNAL_ERROR, + "Memory monitor initialization: invalid memento", null);//$NON-NLS-1$ + throw new CoreException(status); + } + + // Process the block list specific to this memory context + // FIXME: (Bug228573) We only process the first entry... + if (root.getAttribute(ATTR_EXPRESSION_LIST_CONTEXT).equals(fContextString)) { + List<IMemoryBlock> blocks = new ArrayList<IMemoryBlock>(); + NodeList expressionList = root.getChildNodes(); + int length = expressionList.getLength(); + for (int i = 0; i < length; ++i) { + Node node = expressionList.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE) { + Element entry = (Element) node; + if (entry.getNodeName().equalsIgnoreCase(MEMORY_BLOCK_EXPRESSION)) { + String label = entry.getAttribute(ATTR_MEMORY_BLOCK_EXPR_LABEL); + String address = entry.getAttribute(ATTR_MEMORY_BLOCK_EXPR_ADDRESS); + BigInteger blockAddress = new BigInteger(address); + DsfMemoryBlock block = new DsfMemoryBlock(this, memoryCtx, fModelId, label, blockAddress, fWordSize, 0); + blocks.add(block); + } + } + } + DebugPlugin.getDefault().getMemoryBlockManager().addMemoryBlocks( blocks.toArray(new IMemoryBlock[blocks.size()])); + } + } + + // FIXME: (Bug228573) Each retrieval overwrites the previous one :-( + + // In theory, we should make this a Job since we are writing to the file system. + // However, this would cause the same racing condition as Bug228308. Finally, we + // don't care too much about the UI responsiveness since we are in the process of + // shutting down :-) + public void saveMemoryBlocks() { + try { + ILaunchConfigurationWorkingCopy wc = fLaunchConfig.getWorkingCopy(); + wc.setAttribute(ATTR_DEBUGGER_MEMORY_BLOCKS, getMemento()); + wc.doSave(); + } + catch( CoreException e ) { + DsfPlugin.getDefault().getLog().log(e.getStatus()); + } + } + + public String getMemento() throws CoreException { + IMemoryBlock[] blocks = DebugPlugin.getDefault().getMemoryBlockManager().getMemoryBlocks(this); + Document document = DebugPlugin.newDocument(); + Element expressionList = document.createElement(MEMORY_BLOCK_EXPRESSION_LIST); + expressionList.setAttribute(ATTR_EXPRESSION_LIST_CONTEXT, fContextString); + for (IMemoryBlock block : blocks) { + if (block instanceof IMemoryBlockExtension) { + IMemoryBlockExtension memoryBlock = (IMemoryBlockExtension) block; + Element expression = document.createElement(MEMORY_BLOCK_EXPRESSION); + expression.setAttribute(ATTR_MEMORY_BLOCK_EXPR_LABEL, memoryBlock.getExpression()); + expression.setAttribute(ATTR_MEMORY_BLOCK_EXPR_ADDRESS, memoryBlock.getBigBaseAddress().toString()); + expressionList.appendChild(expression); + } + } + document.appendChild(expressionList); + return DebugPlugin.serializeDocument(document); + } + + /////////////////////////////////////////////////////////////////////////// + // Accessors + /////////////////////////////////////////////////////////////////////////// + + public DsfSession getSession() { + return fSession; + } + + public DsfExecutor getExecutor() { + return fExecutor; + } + + public ServiceTracker getServiceTracker() { + return fMemoryServiceTracker; + } + + /////////////////////////////////////////////////////////////////////////// + // Launch/Target specific information + /////////////////////////////////////////////////////////////////////////// + + public ILaunch getLaunch() { + return fLaunch; + } + + public IDebugTarget getDebugTarget() { + return fDebugTarget; + } + + public int getAddressSize() { + return fAddressSize; + } + + public int getAddressableSize() { + return fWordSize; + } + + public boolean supportsValueModification() { + return fSupportsValueModification; + } + + public boolean supportBaseAddressModification() { + return fSupportBaseAddressModification; + } + + /////////////////////////////////////////////////////////////////////////// + // IMemoryBlockRetrieval - obsoleted by IMemoryBlockRetrievalExtension + /////////////////////////////////////////////////////////////////////////// + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#supportsStorageRetrieval() + */ + public boolean supportsStorageRetrieval() { + return true; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#getMemoryBlock(long, + * long) + */ + public IMemoryBlock getMemoryBlock(final long startAddress, final long length) throws DebugException { + throw new DebugException(new Status( + IStatus.ERROR, DsfPlugin.PLUGIN_ID, DebugException.NOT_SUPPORTED, + "getMemoryBlock() not supported, use getExtendedMemoryBlock()", null)); //$NON-NLS-1$ + } + + /////////////////////////////////////////////////////////////////////////// + // IMemoryBlockRetrievalExtension + /////////////////////////////////////////////////////////////////////////// + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension#getExtendedMemoryBlock(java.lang.String, + * java.lang.Object) + */ + public IMemoryBlockExtension getExtendedMemoryBlock(String expression, Object context) throws DebugException { + // Drill for the actual DMC + IMemoryDMContext memoryDmc = null; + IDMContext dmc = null; + if (context instanceof IAdaptable) { + dmc = (IDMContext)((IAdaptable)context).getAdapter(IDMContext.class); + if (dmc != null) { + memoryDmc = DMContexts.getAncestorOfType(dmc, IMemoryDMContext.class); + } + } + + if (memoryDmc == null) { + return null; + } + + // The block start address (supports 64-bit processors) + BigInteger blockAddress; + + /* + * See if the expression is a simple numeric value; if it is, we can + * avoid some costly processing (calling the back-end to resolve the + * expression and obtain an address) + */ + try { + // First, assume a decimal address + int base = 10; + int offset = 0; + + // Check for "hexadecimality" + if (expression.startsWith("0x") || expression.startsWith("0X")) { //$NON-NLS-1$//$NON-NLS-2$ + base = 16; + offset = 2; + } + // Check for "binarity" + else if (expression.startsWith("0b")) { //$NON-NLS-1$ + base = 2; + offset = 2; + } + // Check for "octality" + else if (expression.startsWith("0")) { //$NON-NLS-1$ + base = 8; + offset = 1; + } + // Now, try to parse the expression. If a NumberFormatException is + // thrown, then it wasn't a simple numerical expression and we go + // to plan B (attempt an expression evaluation) + blockAddress = new BigInteger(expression.substring(offset), base); + + } catch (NumberFormatException nfexc) { + // OK, expression is not a simple, absolute numeric value; + // try to resolve as an expression. + // In case of failure, simply return 'null' + + // Resolve the expression + blockAddress = resolveMemoryAddress(dmc, expression); + if (blockAddress == null) { + return null; + } + } + + /* + * At this point, we only resolved the requested memory block + * start address and we have no idea of the block's length. + * + * The renderer will provide this information when it calls + * getBytesFromAddress() i.e. after the memory block holder has + * been instantiated. + * + * The down side is that every time we switch renderer, for the + * same memory block, a trip to the target could result. However, + * the memory request cache should save the day. + */ + + return new DsfMemoryBlock(this, memoryDmc, fModelId, expression, blockAddress, fWordSize, 0); + } + + /////////////////////////////////////////////////////////////////////////// + // Helper functions + /////////////////////////////////////////////////////////////////////////// + + private BigInteger resolveMemoryAddress(final IDMContext dmc, final String expression) throws DebugException { + + // Use a Query to "synchronize" the downstream calls + Query<BigInteger> query = new Query<BigInteger>() { + @Override + protected void execute(final DataRequestMonitor<BigInteger> drm) { + // Lookup for the ExpressionService + final IExpressions expressionService = (IExpressions) fExpressionServiceTracker.getService(); + if (expressionService != null) { + // Create the expression + final IExpressionDMContext expressionDMC = expressionService.createExpression(dmc, expression); + String formatId = IFormattedValues.HEX_FORMAT; + FormattedValueDMContext valueDmc = expressionService.getFormattedValueContext(expressionDMC, formatId); + expressionService.getFormattedExpressionValue( + valueDmc, + new DataRequestMonitor<FormattedValueDMData>(getExecutor(), drm) { + @Override + protected void handleSuccess() { + // Store the result + FormattedValueDMData data = getData(); + String value = data.getFormattedValue().substring(2); // Strip the "0x" + drm.setData(new BigInteger(value, 16)); + drm.done(); + } + } + ); + } + } + }; + fExecutor.execute(query); + + try { + // The happy case + return query.get(); + } catch (InterruptedException e) { + throw new DebugException(new Status(IStatus.ERROR, + DsfPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, + "Error evaluating memory address (InterruptedException).", e)); //$NON-NLS-1$ + + } catch (ExecutionException e) { + throw new DebugException(new Status(IStatus.ERROR, + DsfPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, + "Error evaluating memory address (ExecutionException).", e)); //$NON-NLS-1$ + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/AbstractDsfDebugServicesFactory.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/AbstractDsfDebugServicesFactory.java new file mode 100644 index 00000000000..f93655ba9d2 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/AbstractDsfDebugServicesFactory.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2008 Ericsson and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.service; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControl; +import org.eclipse.cdt.dsf.service.DsfSession; + +/** + * Convenience base class for {@link IDsfDebugServicesFactory} + * @since 1.1 + */ +public abstract class AbstractDsfDebugServicesFactory implements IDsfDebugServicesFactory { + + @SuppressWarnings("unchecked") + public <V> V createService(Class<V> clazz, DsfSession session, Object ... optionalArguments) { + if (IBreakpoints.class.isAssignableFrom(clazz)) { + return (V)createBreakpointService(session); + } else if (ICommandControl.class.isAssignableFrom(clazz)) { + return (V)createCommandControl(session); + } else if (IDisassembly.class.isAssignableFrom(clazz)) { + return (V)createDisassemblyService(session); + } else if (IExpressions.class.isAssignableFrom(clazz)) { + return (V)createExpressionService(session); + } else if (IMemory.class.isAssignableFrom(clazz)) { + return (V)createMemoryService(session); + } else if (IModules.class.isAssignableFrom(clazz)) { + return (V)createModulesService(session); + } else if (IProcesses.class.isAssignableFrom(clazz)) { + return (V)createProcessesService(session); + } else if (IRegisters.class.isAssignableFrom(clazz)) { + return (V)createRegistersService(session); + } else if (IRunControl.class.isAssignableFrom(clazz)) { + return (V)createRunControlService(session); + } else if (ISourceLookup.class.isAssignableFrom(clazz)) { + return (V)createSourceLookupService(session); + } else if (ISignals.class.isAssignableFrom(clazz)) { + return (V)createSignalsService(session); + } else if (IStack.class.isAssignableFrom(clazz)) { + return (V)createStackService(session); + } else if (ISymbols.class.isAssignableFrom(clazz)) { + return (V)createSymbolsService(session); + } + + return null; + } + + protected IBreakpoints createBreakpointService(DsfSession session) { return null; } + protected ICommandControl createCommandControl(DsfSession session) { return null; } + protected IDisassembly createDisassemblyService(DsfSession session) { return null; } + protected IExpressions createExpressionService(DsfSession session) { return null; } + protected IMemory createMemoryService(DsfSession session) { return null; } + protected IModules createModulesService(DsfSession session) { return null; } + protected IProcesses createProcessesService(DsfSession session) { return null; } + protected IRegisters createRegistersService(DsfSession session) { return null; } + protected IRunControl createRunControlService(DsfSession session) { return null; } + protected ISourceLookup createSourceLookupService(DsfSession session) { return null; } + protected ISignals createSignalsService(DsfSession session) { return null; } + protected IStack createStackService(DsfSession session) { return null; } + protected ISymbols createSymbolsService(DsfSession session) { return null; } + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/BreakpointsMediator.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/BreakpointsMediator.java new file mode 100644 index 00000000000..864c3400f20 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/BreakpointsMediator.java @@ -0,0 +1,886 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River 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 - Initial API and implementation + * Ericsson - Low-level breakpoints integration + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMContext; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.resources.IMarkerDelta; +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.Job; +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.model.IBreakpoint; +import org.osgi.framework.BundleContext; + +/** + * + */ +public class BreakpointsMediator extends AbstractDsfService implements IBreakpointManagerListener, IBreakpointListener +{ + + /** + * The attribute translator that this service will use to map the platform + * breakpiont attributes to the corresponding target attributes, and vice + * versa. + */ + private IBreakpointAttributeTranslator fAttributeTranslator; + + /** + * DSF Debug service for creating breakpoints. + */ + IBreakpoints fBreakpoints; + + /** + * Platform breakpoint manager + */ + IBreakpointManager fBreakpointManager; + + + /////////////////////////////////////////////////////////////////////////// + // Breakpoints tracking + /////////////////////////////////////////////////////////////////////////// + + /** + * Holds the set of platform breakpoints with their corresponding back-end + * breakpoint attributes, per context (i.e. each platform breakpoint is + * replicated for each execution context). + * - Context entry added/removed on start/stopTrackingBreakpoints() + * - Augmented on breakpointAdded() + * - Modified on breakpointChanged() + * - Diminished on breakpointRemoved() + */ + private Map<IBreakpointsTargetDMContext, Map<IBreakpoint, List<Map<String, Object>>>> fPlatformBPs = + new HashMap<IBreakpointsTargetDMContext, Map<IBreakpoint, List<Map<String, Object>>>>(); + + /** + * Holds the mapping from platform breakpoint to the corresponding target + * breakpoint(s), per context. There can be multiple back-end BPs for a + * single platform BP in the case of [1] multiple target contexts, and/or + * [2] thread filtering. + * Updated when: + * - We start/stop tracking an execution context + * - A platform breakpoint is added/removed + * - A thread filter is applied/removed + */ + private Map<IBreakpointsTargetDMContext, Map<IBreakpoint, List<IBreakpointDMContext>>> fBreakpointDMContexts = + new HashMap<IBreakpointsTargetDMContext, Map<IBreakpoint, List<IBreakpointDMContext>>>(); + + /** + * Due to the very asynchronous nature of DSF, a new breakpoint request can + * pop up at any time before an ongoing one is completed. The following set + * is used to store requests until the ongoing operation completes. + */ + private Set<IBreakpoint> fPendingRequests = new HashSet<IBreakpoint>(); + + /** + * @see fPendingRequests + */ + private Set<IBreakpoint> fPendingBreakpoints = new HashSet<IBreakpoint>(); + + /////////////////////////////////////////////////////////////////////////// + // AbstractDsfService + /////////////////////////////////////////////////////////////////////////// + + /** + * The service constructor + * + * @param session + * @param debugModelId + */ + public BreakpointsMediator(DsfSession session, IBreakpointAttributeTranslator attributeTranslator) { + super(session); + fAttributeTranslator = attributeTranslator; + } + + @Override + public void initialize(final RequestMonitor rm) { + // - Collect references for the services we interact with + // - Register to interesting events + // - Obtain the list of platform breakpoints + // - Register the service for interested parties + super.initialize( + new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + doInitialize(rm); + }}); + } + + /** + * Asynchronous service initialization + * + * @param requestMonitor + */ + private void doInitialize(RequestMonitor rm) { + + // Get the services references + fBreakpoints = getServicesTracker().getService(IBreakpoints.class); + fBreakpointManager = DebugPlugin.getDefault().getBreakpointManager(); + fAttributeTranslator.initialize(this); + + // Register to the useful events + fBreakpointManager.addBreakpointListener(this); + fBreakpointManager.addBreakpointManagerListener( this ); + + // Register this service + register(new String[] { BreakpointsMediator.class.getName() }, + new Hashtable<String, String>()); + + rm.done(); + } + + @Override + public void shutdown(final RequestMonitor rm) { + // - Un-register the service + // - Stop listening to events + // - Remove the breakpoints installed by this service + // + // Since we are shutting down, there is no overwhelming need + // to keep the maps coherent... + + // Stop accepting requests and events + unregister(); + fBreakpointManager.removeBreakpointListener(this); + fBreakpointManager.removeBreakpointManagerListener( this ); + fAttributeTranslator.dispose(); + + // Cleanup the breakpoints that are still installed by the service. + // Use a counting monitor which will call mom to complete the shutdown + // after the breakpoints are un-installed (successfully or not). + CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), rm) { + @Override + protected void handleCompleted() { + BreakpointsMediator.super.shutdown(rm); + } + }; + + // We have to make a copy of the fPlatformBPs keys because uninstallBreakpoints() + // modifies the map as it walks through it. + List<IBreakpointsTargetDMContext> platformBPKeysCopy = new ArrayList<IBreakpointsTargetDMContext>(fPlatformBPs.size()); + platformBPKeysCopy.addAll(0, fPlatformBPs.keySet()); + for (IBreakpointsTargetDMContext dmc : platformBPKeysCopy) { + stopTrackingBreakpoints(dmc, countingRm); + } + countingRm.setDoneCount(platformBPKeysCopy.size()); + } + + @Override + protected BundleContext getBundleContext() { + return DsfPlugin.getBundleContext(); + } + + /////////////////////////////////////////////////////////////////////////// + // IBreakpointsManager + /////////////////////////////////////////////////////////////////////////// + + + /** + * Install and begin tracking breakpoints for given context. The service + * will keep installing new breakpoints that appear in the IDE for this + * context until {@link #uninstallBreakpoints(IDMContext)} is called for that + * context. + * @param dmc Context to start tracking breakpoints for. + * @param rm Completion callback. + */ + public void startTrackingBreakpoints(IBreakpointsTargetDMContext dmc, final RequestMonitor rm) { + // - Augment the maps with the new execution context + // - Install the platform breakpoints on the selected target + + // Validate the context + final IBreakpointsTargetDMContext breakpointsDmc = DMContexts.getAncestorOfType(dmc, IBreakpointsTargetDMContext.class); + if (breakpointsDmc == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid context type", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Make sure a mapping for this execution context does not already exist + Map<IBreakpoint, List<Map<String, Object>>> platformBPs = fPlatformBPs.get(dmc); + if (platformBPs != null) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INTERNAL_ERROR, "Context already initialized", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Create entries in the breakpoint tables for the new context. These entries should only + // be removed when this service stops tracking breakpoints for the given context. + fPlatformBPs.put(breakpointsDmc, new HashMap<IBreakpoint, List<Map<String, Object>>>()); + fBreakpointDMContexts.put(breakpointsDmc, new HashMap<IBreakpoint, List<IBreakpointDMContext>>()); + + // Install the platform breakpoints (stored in fPlatformBPs) on the target. + // We need to use a background thread for this operation because we are + // accessing the resources system to retrieve the breakpoint attributes. + // Accessing the resources system potentially requires using global locks. + // Also we will be calling IBreakpointAttributeTranslator which is prohibited + // from being called on the session executor thread. + new Job("MI Debugger: Install initial breakpoint list.") { //$NON-NLS-1$ + { setSystem(true); } + + // Get the stored breakpoints from the platform BreakpointManager + // and install them on the target + @Override + protected IStatus run(IProgressMonitor monitor) { + // Read initial breakpoints from platform. Copy the breakpoint attributes into a local map. + // Note that we cannot write data into fPlatformBPs table here directly because we are not + // executing on the dispatch thread. + final Map<IBreakpoint, List<Map<String, Object>>> initialPlatformBPs = + new HashMap<IBreakpoint, List<Map<String, Object>>>(); + try { + // Get the stored breakpoint list from the platform BreakpointManager + IBreakpoint[] bps = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(); + // Single out the installable breakpoints... + for (IBreakpoint bp : bps) { + if (fAttributeTranslator.supportsBreakpoint(bp)) { + // Retrieve the breakpoint attributes + List<Map<String, Object>> attrsArray = + fAttributeTranslator.getBreakpointAttributes(bp, fBreakpointManager.isEnabled()); + // Store it for now (will be installed on the dispatcher thread) + initialPlatformBPs.put(bp, attrsArray); + } + } + } catch (CoreException e) { + IStatus status = new Status( + IStatus.ERROR, DsfPlugin.PLUGIN_ID, REQUEST_FAILED, "Unable to read initial breakpoint attributes", e); //$NON-NLS-1$ + rm.setStatus(status); + rm.done(); + return status; + } + + // Submit the runnable to plant the breakpoints on dispatch thread. + getExecutor().submit(new Runnable() { + public void run() { + installInitialBreakpoints(breakpointsDmc, initialPlatformBPs, rm); + } + }); + + return Status.OK_STATUS; + } + }.schedule(); + } + + /** + * Installs the breakpoints that existed prior to the activation of this + * breakpoints context. + */ + private void installInitialBreakpoints(final IBreakpointsTargetDMContext dmc, + Map<IBreakpoint, List<Map<String, Object>>> initialPlatformBPs, + RequestMonitor rm) + { + // Retrieve the set of platform breakpoints for this context + Map<IBreakpoint, List<Map<String, Object>>> platformBPs = fPlatformBPs.get(dmc); + if (platformBPs == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Install the individual breakpoints on the executor thread + // Requires a counting monitor to know when we're done + final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), rm); + countingRm.setDoneCount(initialPlatformBPs.size()); + + for (final IBreakpoint bp : initialPlatformBPs.keySet()) { + final List<Map<String, Object>> attrs = initialPlatformBPs.get(bp); + // Upon determining the debuggerPath, the breakpoint is installed + installBreakpoint(dmc, bp, attrs, new RequestMonitor(getExecutor(), countingRm)); + } + } + + + public void stopTrackingBreakpoints(final IBreakpointsTargetDMContext dmc, final RequestMonitor rm) { + // - Remove the target breakpoints for the given execution context + // - Update the maps + + // Remove the breakpoints for given DMC from the internal maps. + Map<IBreakpoint, List<Map<String, Object>>> platformBPs = fPlatformBPs.get(dmc); + if (platformBPs == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INTERNAL_ERROR, "Breakpoints not installed for given context", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Uninstall the individual breakpoints on the executor thread + // Requires a counting monitor to know when we're done + final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), rm); + countingRm.setDoneCount(platformBPs.size()); + + for (final IBreakpoint bp : platformBPs.keySet()) { + uninstallBreakpoint(dmc, bp, + new RequestMonitor(getExecutor(), countingRm) { + @Override + protected void handleCompleted() { + // After the breakpoint is removed from target. Call the attribute + // translator to refresh breakpoint status based on the new target + // breakpoint status. + new Job("Breakpoint status update") { //$NON-NLS-1$ + { setSystem(true); } + @Override + protected IStatus run(IProgressMonitor monitor) { + fAttributeTranslator.updateBreakpointStatus(bp); + return Status.OK_STATUS; + }; + }.schedule(); + + countingRm.done(); + } + }); + } + } + + /////////////////////////////////////////////////////////////////////////// + // Back-end interface functions + /////////////////////////////////////////////////////////////////////////// + + /** + * Install a new platform breakpoint on the back-end. A platform breakpoint + * can resolve into multiple back-end breakpoints when threads are taken + * into account. + * + * @param dmc + * @param breakpoint + * @param attrsList + * @param rm + */ + private void installBreakpoint(IBreakpointsTargetDMContext dmc, final IBreakpoint breakpoint, + final List<Map<String, Object>> attrsList, final RequestMonitor rm) + { + // Retrieve the set of breakpoints for this context + final Map<IBreakpoint, List<Map<String, Object>>> platformBPs = fPlatformBPs.get(dmc); + assert platformBPs != null; + + final Map<IBreakpoint, List<IBreakpointDMContext>> breakpointIDs = fBreakpointDMContexts.get(dmc); + assert breakpointIDs != null; // fBreakpointIds should be updated in parallel with fPlatformBPs + + // Ensure the breakpoint is not already installed + if (platformBPs.containsKey(breakpoint)) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_STATE, "Breakpoint already installed", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Update the breakpoint status when all back-end breakpoints have been installed + final CountingRequestMonitor installRM = new CountingRequestMonitor(getExecutor(), rm) { + @Override + protected void handleCompleted() { + // Store the platform breakpoint + platformBPs.put(breakpoint, attrsList); + new Job("Breakpoint status update") { //$NON-NLS-1$ + { setSystem(true); } + @Override + protected IStatus run(IProgressMonitor monitor) { + fAttributeTranslator.updateBreakpointStatus(breakpoint); + return Status.OK_STATUS; + }; + }.schedule(); + rm.done(); + } + }; + + // A back-end breakpoint needs to be installed for each specified attributes map. + installRM.setDoneCount(attrsList.size()); + + // Install the back-end breakpoint(s) + for (Map<String, Object> attrs : attrsList) { + fBreakpoints.insertBreakpoint( + dmc, attrs, + new DataRequestMonitor<IBreakpointDMContext>(getExecutor(), installRM) { + @Override + protected void handleCompleted() { + List<IBreakpointDMContext> list = breakpointIDs.get(breakpoint); + if (list == null) { + list = new LinkedList<IBreakpointDMContext>(); + breakpointIDs.put(breakpoint, list); + } + + if (isSuccess()) { + // Add the breakpoint back-end mapping + list.add(getData()); + } else { + // TODO (bug 219841): need to add breakpoint error status tracking + // in addition to fBreakpointDMContexts. + } + installRM.done(); + } + }); + } + } + + /** + * Un-install an individual breakpoint on the back-end. For one platform + * breakpoint, there could be multiple corresponding back-end breakpoints. + * + * @param dmc + * @param breakpoint + * @param rm + */ + private void uninstallBreakpoint(final IBreakpointsTargetDMContext dmc, final IBreakpoint breakpoint, + final RequestMonitor rm) + { + // Remove completion monitor + CountingRequestMonitor removeRM = new CountingRequestMonitor(getExecutor(), rm) { + @Override + protected void handleCompleted() { + // Remove the attributes mapping + Map<IBreakpoint, List<Map<String, Object>>> platformBPs = fPlatformBPs.get(dmc); + if (platformBPs == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$ + rm.done(); + return; + } + platformBPs.remove(breakpoint); + + // Remove the back-end mapping + Map<IBreakpoint, List<IBreakpointDMContext>> breakpointIDs = fBreakpointDMContexts.get(dmc); + if (breakpointIDs == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid breakpoint", null)); //$NON-NLS-1$ + rm.done(); + return; + } + breakpointIDs.get(breakpoint).clear(); + breakpointIDs.remove(breakpoint); + + // Update breakpoint status + new Job("Breakpoint status update") { //$NON-NLS-1$ + { setSystem(true); } + @Override + protected IStatus run(IProgressMonitor monitor) { + fAttributeTranslator.updateBreakpointStatus(breakpoint); + return Status.OK_STATUS; + }; + }.schedule(); + + rm.done(); + } + }; + + // Remove the back-end breakpoints + Map<IBreakpoint, List<IBreakpointDMContext>> breakpointIDs = fBreakpointDMContexts.get(dmc); + if (breakpointIDs == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid breakpoint", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + List<IBreakpointDMContext> list = breakpointIDs.get(breakpoint); + int count = 0; + if (list != null) { + for (IBreakpointDMContext bp : list) { + fBreakpoints.removeBreakpoint(bp, removeRM); + } + count = list.size(); + } + removeRM.setDoneCount(count); + } + + /** + * Modify an individual breakpoint + * + * @param context + * @param breakpoint + * @param attributes + * @param rm + * @throws CoreException + */ + private void modifyBreakpoint(final IBreakpointsTargetDMContext context, final IBreakpoint breakpoint, + final List<Map<String, Object>> newAttrsList0, final IMarkerDelta oldValues, final RequestMonitor rm) + { + // This method uses several lists to track the changed breakpoints: + // commonAttrsList - attributes which have not changed + // oldAttrsList - attributes for the breakpoint before the change + // newAttrsList - attributes for the breakpoint after the change + // oldBpContexts - target-side breakpoints from before the change + // newBpContexts - target-side breakpoints after the change + // attrDeltasList - changes in the attributes for each attribute map in + // oldAttrsList and newAttrsList + + // Get the maps + final Map<IBreakpoint, List<Map<String, Object>>> platformBPs = fPlatformBPs.get(context); + final Map<IBreakpoint, List<IBreakpointDMContext>> breakpointIDs = fBreakpointDMContexts.get(context); + if (platformBPs == null || breakpointIDs == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Get the original breakpoint attributes + final List<Map<String, Object>> oldAttrsList0 = platformBPs.get(breakpoint); + if (oldAttrsList0 == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid breakpoint", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Get the list of corresponding back-end breakpoints + final List<IBreakpointDMContext> oldBpContexts = new ArrayList<IBreakpointDMContext>(breakpointIDs.get(breakpoint)); + if (oldBpContexts == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid breakpoint", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Calculate the list of attributes maps that have not changed. + // Immediately add these to the list of new breakpoint contexts, + // and remove them from further breakpoint attribute comparisons. + final List<Map<String, Object>> commonAttrsList = getCommonAttributeMaps(newAttrsList0, oldAttrsList0); + final List<IBreakpointDMContext> newBpContexts = new ArrayList<IBreakpointDMContext>(commonAttrsList.size()); + + final List<Map<String, Object>> newAttrsList = new ArrayList<Map<String, Object>>(newAttrsList0); + newAttrsList.removeAll(commonAttrsList); + + List<Map<String, Object>> oldAttrsList = new ArrayList<Map<String, Object>>(oldAttrsList0); + for (int i = 0; i < oldAttrsList.size(); i++) { + if (commonAttrsList.contains(oldAttrsList.get(i))) { + if (oldBpContexts.size() > i) { + newBpContexts.add(oldBpContexts.remove(i)); + } + } + } + oldAttrsList.removeAll(commonAttrsList); + + // Create a list of attribute changes. The lenghth of this list will + // always be max(oldAttrList.size(), newAttrsList.size()), padded with + // null's if oldAttrsList was longer. + final List<Map<String, Object>> attrDeltasList = getAttributesDeltas(oldAttrsList, newAttrsList); + + // Create the request monitor that will be called when all + // modifying/inserting/removing is complete. + final CountingRequestMonitor countingRM = new CountingRequestMonitor(getExecutor(), rm) { + @Override + protected void handleCompleted() { + // Save the new list of breakpoint contexts and attributes + breakpointIDs.put(breakpoint, newBpContexts); + newAttrsList.addAll(commonAttrsList); + platformBPs.put(breakpoint, newAttrsList); + + // Update breakpoint status. updateBreakpointStatus() cannot + // be called on the executor thread, so we need to + // use a Job. + new Job("Breakpoint status update") { //$NON-NLS-1$ + { setSystem(true); } + @Override + protected IStatus run(IProgressMonitor monitor) { + fAttributeTranslator.updateBreakpointStatus(breakpoint); + return Status.OK_STATUS; + }; + }.schedule(); + + super.handleCompleted(); + } + }; + + // Set the count, if could be zero if no breakpoints have actually changed. + countingRM.setDoneCount(attrDeltasList.size()); + + // Process the changed breakpoints. + for (int i = 0; i < attrDeltasList.size(); i++) { + if (attrDeltasList.get(i) == null) { + // The list of new attribute maps was shorter than the old. + // Remove the corresponding target-side bp. + fBreakpoints.removeBreakpoint(oldBpContexts.get(i), countingRM); + } else if ( i >= oldBpContexts.size()) { + // The list of new attribute maps was longer, just insert + // the new breakpoint + final Map<String, Object> attrs = newAttrsList.get(i); + fBreakpoints.insertBreakpoint( + context, attrs, + new DataRequestMonitor<IBreakpointDMContext>(getExecutor(), countingRM) { + @Override + protected void handleSuccess() { + newBpContexts.add(getData()); + countingRM.done(); + } + }); + } else if ( !fAttributeTranslator.canUpdateAttributes(oldBpContexts.get(i), attrDeltasList.get(i)) ) { + // The attribute translator tells us that the debugger cannot modify the + // breakpoint to change the given attributes. Remove the breakpoint + // and insert a new one. + final Map<String, Object> attrs = newAttrsList.get(i); + fBreakpoints.removeBreakpoint( + oldBpContexts.get(i), + new RequestMonitor(getExecutor(), countingRM) { + @Override + protected void handleCompleted() { + fBreakpoints.insertBreakpoint( + context, attrs, + new DataRequestMonitor<IBreakpointDMContext>(getExecutor(), countingRM) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + newBpContexts.add(getData()); + } else { + // TODO (bug 219841): need to add breakpoint error status tracking + // in addition to fBreakpointDMContexts. + } + countingRM.done(); + } + }); + } + }); + } else { + // The back end can modify the breakpoint. Update the breakpoint with the + // new attributes. + final IBreakpointDMContext bpCtx = oldBpContexts.get(i); + fBreakpoints.updateBreakpoint( + oldBpContexts.get(i), newAttrsList.get(i), + new RequestMonitor(getExecutor(), countingRM) { + @Override + protected void handleSuccess() { + newBpContexts.add(bpCtx); + countingRM.done(); + } + }); + } + } + } + + private List<Map<String, Object>> getCommonAttributeMaps(List<Map<String, Object>> array1, List<Map<String, Object>> array2) + { + List<Map<String, Object>> intersection = new LinkedList<Map<String, Object>>(); + List<Map<String, Object>> list2 = new ArrayList<Map<String, Object>>(array2); + for (Map<String, Object> array1Map : array1) { + if (list2.remove(array1Map)) { + intersection.add(array1Map); + } + } + return intersection; + } + + /** + * Determine the set of modified attributes + * + * @param oldAttributes + * @param newAttributes + * @return + */ + private List<Map<String, Object>> getAttributesDeltas(List<Map<String, Object>> oldAttributesList, List<Map<String, Object>> newAttributesList) { + List<Map<String, Object>> deltas = new ArrayList<Map<String, Object>>(oldAttributesList.size()); + + // Go through the bp attributes common to the old and the new lists and calculate + // their deltas. + for (int i = 0; i < oldAttributesList.size() && i < newAttributesList.size(); i++) { + Map<String, Object> oldAttributes = oldAttributesList.get(i); + Map<String, Object> newAttributes = newAttributesList.get(i); + + Map<String, Object> delta = new HashMap<String, Object>(); + + Set<String> oldKeySet = oldAttributes.keySet(); + Set<String> newKeySet = newAttributes.keySet(); + + Set<String> commonKeys = new HashSet<String>(newKeySet); commonKeys.retainAll(oldKeySet); + Set<String> addedKeys = new HashSet<String>(newKeySet); addedKeys.removeAll(oldKeySet); + Set<String> removedKeys = new HashSet<String>(oldKeySet); removedKeys.removeAll(newKeySet); + + // Add the modified attributes + for (String key : commonKeys) { + if (!(oldAttributes.get(key).equals(newAttributes.get(key)))) + delta.put(key, newAttributes.get(key)); + } + + // Add the new attributes + for (String key : addedKeys) { + delta.put(key, newAttributes.get(key)); + } + + // Remove the deleted attributes + for (String key : removedKeys) { + delta.put(key, null); + } + deltas.add(delta); + } + + // Add all the new attributes as deltas + for (int i = deltas.size(); i < newAttributesList.size(); i++) { + deltas.add(newAttributesList.get(i)); + } + + // For any old attribute Maps that were removed, insert a null value in the deltas list. + for (int i = deltas.size(); i < oldAttributesList.size(); i++) { + deltas.add(null); + } + + return deltas; + } + + /////////////////////////////////////////////////////////////////////////// + // IBreakpointManagerListener implementation + /////////////////////////////////////////////////////////////////////////// + + public void breakpointManagerEnablementChanged(boolean enabled) { + for (IBreakpoint breakpoint : fBreakpointManager.getBreakpoints()) { + breakpointChanged(breakpoint, null); + } + } + + @ThreadSafe + public void breakpointAdded(final IBreakpoint breakpoint) { + if (fAttributeTranslator.supportsBreakpoint(breakpoint)) { + try { + // Retrieve the breakpoint attributes + final List<Map<String, Object>> attrsArray = + fAttributeTranslator.getBreakpointAttributes(breakpoint, fBreakpointManager.isEnabled()); + + getExecutor().execute(new DsfRunnable() { + public void run() { + //TODO pp: need to track pending requests + + final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), null) { + @Override + protected void handleError() { + if (getStatus().getSeverity() == IStatus.ERROR) { + DsfPlugin.getDefault().getLog().log(getStatus()); + } + } + }; + countingRm.setDoneCount(fPlatformBPs.size()); + + // Install the breakpoint in all the execution contexts + for (final IBreakpointsTargetDMContext dmc : fPlatformBPs.keySet()) { + installBreakpoint(dmc, breakpoint, attrsArray, new RequestMonitor(getExecutor(), countingRm)); + } + } + }); + } catch (CoreException e) { + DsfPlugin.getDefault().getLog().log(e.getStatus()); + } catch (RejectedExecutionException e) { + } + } + + } + + /////////////////////////////////////////////////////////////////////////// + // IBreakpointListener implementation + /////////////////////////////////////////////////////////////////////////// + + public void breakpointChanged(final IBreakpoint breakpoint, final IMarkerDelta delta) { + if (fAttributeTranslator.supportsBreakpoint(breakpoint)) { + try { + // Retrieve the breakpoint attributes + final List<Map<String, Object>> attrsArray = + fAttributeTranslator.getBreakpointAttributes(breakpoint, fBreakpointManager.isEnabled()); + + // Modify the breakpoint in all the target contexts + getExecutor().execute( new DsfRunnable() { + public void run() { + + // If the breakpoint is currently being updated, queue the request and exit + if (fPendingRequests.contains(breakpoint)) { + fPendingBreakpoints.add(breakpoint); + return; + } + + // Keep track of the updates + final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), null) { + @Override + protected void handleCompleted() { + + if (!isSuccess()) { + if (getStatus().getSeverity() == IStatus.ERROR) { + DsfPlugin.getDefault().getLog().log(getStatus()); + } + } + + // Indicate that the pending request has completed + fPendingRequests.remove(breakpoint); + + // Process the next pending update for this breakpoint + if (fPendingBreakpoints.contains(breakpoint)) { + fPendingBreakpoints.remove(breakpoint); + new Job("Deferred breakpoint changed job") { //$NON-NLS-1$ + { setSystem(true); } + @Override + protected IStatus run(IProgressMonitor monitor) { + breakpointChanged(breakpoint, delta); + return Status.OK_STATUS; + }; + }.schedule(); + } + } + }; + countingRm.setDoneCount(fPlatformBPs.size()); + + // Mark the breakpoint as being updated and go + fPendingRequests.add(breakpoint); + + // Modify the breakpoint in all the execution contexts + for (final IBreakpointsTargetDMContext dmc : fPlatformBPs.keySet()) { + modifyBreakpoint(dmc, breakpoint, attrsArray, delta, new RequestMonitor(getExecutor(), countingRm)); + } + } + }); + } catch (CoreException e) { + DsfPlugin.getDefault().getLog().log(e.getStatus()); + } catch (RejectedExecutionException e) { + } + } + + } + + public void breakpointRemoved(final IBreakpoint breakpoint, IMarkerDelta delta) { + + if (fAttributeTranslator.supportsBreakpoint(breakpoint)) { + try { + getExecutor().execute(new DsfRunnable() { + public void run() { + //TODO pp: need to track pending requests + + CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), null) { + @Override + protected void handleError() { + if (getStatus().getSeverity() == IStatus.ERROR) { + DsfPlugin.getDefault().getLog().log(getStatus()); + } + } + }; + countingRm.setDoneCount(fPlatformBPs.size()); + + // Remove the breakpoint in all the execution contexts + for (IBreakpointsTargetDMContext dmc : fPlatformBPs.keySet()) { + if (fPlatformBPs.get(dmc).remove(breakpoint) != null) { + uninstallBreakpoint(dmc, breakpoint, countingRm); + } else { + // Breakpoint not installed for given context, do nothing. + } + } + } + }); + } catch (RejectedExecutionException e) { + } + } + + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpointAttributeTranslator.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpointAttributeTranslator.java new file mode 100644 index 00000000000..fe158fb3073 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpointAttributeTranslator.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems 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.cdt.dsf.debug.service; + +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.ThreadSafeAndProhibitedFromDsfExecutor; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMContext; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.model.IBreakpoint; + + +@ThreadSafeAndProhibitedFromDsfExecutor("") +public interface IBreakpointAttributeTranslator { + + public void initialize(BreakpointsMediator mediator); + + public void dispose(); + + public List<Map<String, Object>> getBreakpointAttributes(IBreakpoint breakpoint, boolean bpManagerEnabled) throws CoreException; + + public boolean canUpdateAttributes(IBreakpointDMContext bp, Map<String, Object> delta); + + public boolean supportsBreakpoint(IBreakpoint bp); + + public void updateBreakpointStatus(IBreakpoint bp); +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpoints.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpoints.java new file mode 100644 index 00000000000..1fca358fa57 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpoints.java @@ -0,0 +1,140 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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 + * Ericsson - Revisited the API + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.service; + +import java.util.Map; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.service.IDsfService; + +/** + * Breakpoint service interface + */ +public interface IBreakpoints extends IDsfService { + + + /** + * Marker interface for a context for which breakpoints can be installed + */ + public interface IBreakpointsTargetDMContext extends IDMContext {} + + /** + * Specific breakpoint context + */ + @Immutable + public interface IBreakpointDMContext extends IDMContext {} + + /** + * Breakpoint events + */ + public interface IBreakpointsChangedEvent extends IDMEvent<IBreakpointsTargetDMContext> { + public IBreakpointDMContext[] getBreakpoints(); + } + + public interface IBreakpointsAddedEvent extends IBreakpointsChangedEvent {} + public interface IBreakpointsUpdatedEvent extends IBreakpointsChangedEvent {} + public interface IBreakpointsRemovedEvent extends IBreakpointsChangedEvent {} + + /** + * Effective breakpoint data as held by the back-end. + */ + public interface IBreakpointDMData { + + public String getBreakpointType(); + public String getFileName(); + public int getLineNumber(); + public String getFunctionName(); + public IAddress[] getAddresses(); + public String getCondition(); + public int getIgnoreCount(); + public boolean isEnabled(); + public String getExpression(); + } + + /** + * Retrieves the list of breakpoints installed in the context. + * + * Use getBreakpointDMData() to retrieve individual breakpoints. + * + * @param context the execution context of the breakpoint + * @param drm the list of breakpoints in the execution context + */ + public void getBreakpoints(IBreakpointsTargetDMContext context, + DataRequestMonitor<IBreakpointDMContext[]> drm); + + /** + * Retrieves a specific breakpoint from the service. + * + * @param dmc the breakpoint reference + * @param drm the DRM returning the breakpoint data + */ + public void getBreakpointDMData(IBreakpointDMContext dmc, + DataRequestMonitor<IBreakpointDMData> drm); + + /** + * Adds a breakpoint on the target. + * + * The breakpoint context is returned in the DRM. The actual breakpoint + * object can be later be retrieved using getBreakpoint(bp_context). + * + * E.g.: + * IBreakpointDMContext ref = insertBreakpoint(...); + * IBreakpointDMData bp = getBreakpointDMData(ref); + * + * If the breakpoint is a duplicate (already set previously), then it is up to + * the back-end to decide if it is an error or not. + * + * @param context the execution context of the breakpoint + * @param attributes the breakpoint attributes + * @param drm the DRM returning the breakpoint reference + */ + public void insertBreakpoint(IBreakpointsTargetDMContext context, + Map<String,Object> attributes, + DataRequestMonitor<IBreakpointDMContext> drm); + + /** + * Removes the breakpoint on the target. + * + * If the breakpoint doesn't exist, silently ignore it. + * + * @param dmc the context of the breakpoints to remove + * @param rm the asynchronous request monitor + */ + public void removeBreakpoint(IBreakpointDMContext dmc, + RequestMonitor rm); + + /** + * Updates the breakpoint properties on the target. + * + * To add/update/remove a property, simply create a map with + * the desired value(s) for the given key(s). + * + * Properties that affect the breakpoint nature or location + * should not be updated. Instead, the breakpoint should be + * removed then re-inserted. + * + * A null value is used for removal of a property e.g.: + * delta.set(some_key, null); + * + * @param delta the delta properties + * @param dmc the context of the breakpoints to modify + * @param rm the asynchronous request monitor + */ + public void updateBreakpoint(IBreakpointDMContext dmc, + Map<String,Object> delta, RequestMonitor drm); + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ICachingService.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ICachingService.java new file mode 100644 index 00000000000..35d61287299 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ICachingService.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems 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.cdt.dsf.debug.service; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; + +/** + * Interface for services which use an internal cache for data. + * @since 1.1 + */ +public interface ICachingService { + + /** + * Clears the service cache entries which have the given context in their + * hierarchy. + * @param context Root context to flush. May be <code>null</code> to flush + * the entire cache. + */ + public void flushCache(IDMContext context); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IDisassembly.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IDisassembly.java new file mode 100644 index 00000000000..0f751e32aac --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IDisassembly.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2008 Ericsson and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ericsson - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.service; + +import java.math.BigInteger; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.service.IDsfService; + +/** + * Disassembly service interface + */ +public interface IDisassembly extends IDsfService { + + public interface IDisassemblyDMContext extends IDMContext {} + + /** + * Gets the disassembled code from an address range. + * If [startAddress] == null, disassemble from the instruction pointer. + * + * @param context Context of the disassembly code + * @param startAddress Beginning address + * @param endAddress End address + * @param drm Disassembled code + */ + public void getInstructions( + IDisassemblyDMContext context, + BigInteger startAddress, + BigInteger endAddress, + DataRequestMonitor<IInstruction[]> drm); + + /** + * Gets the disassembled code from a file location. + * If [lines] == -1, the whole function is disassembled. + * + * @param context Context of the disassembly code + * @param filename File to disassemble + * @param linenum Line number within the file + * @param lines Number of lines of disassembled code to produce + * @param drm Disassembled code + */ + public void getInstructions( + IDisassemblyDMContext context, + String filename, + int linenum, + int lines, + DataRequestMonitor<IInstruction[]> drm); + + /** + * Gets the mixed disassembled code from an address range. + * If [startAddress] == null, disassemble from the instruction pointer. + * + * @param context Context of the disassembly code + * @param startAddress Beginning address + * @param endAddress End address + * @param drm Disassembled code + */ + public void getMixedInstructions( + IDisassemblyDMContext context, + BigInteger startAddress, + BigInteger endAddress, + DataRequestMonitor<IMixedInstruction[]> drm); + + /** + * Gets the mixed disassembled code from a file location. + * If [lines] == -1, the whole function is disassembled. + * + * @param context Context of the disassembly code + * @param filename File to disassemble + * @param linenum Line number within the file + * @param lines Number of lines of disassembled code to produce + * @param drm Disassembled code + */ + public void getMixedInstructions( + IDisassemblyDMContext context, + String filename, + int linenum, + int lines, + DataRequestMonitor<IMixedInstruction[]> drm); + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IDsfBreakpointExtension.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IDsfBreakpointExtension.java new file mode 100644 index 00000000000..e71d8091b2c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IDsfBreakpointExtension.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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.cdt.dsf.debug.service; + +import org.eclipse.cdt.debug.core.model.ICBreakpointExtension; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.core.runtime.CoreException; + +/** + * An extension to {@link ICBreakpoint} with model-specific breakpoint + * attributes. Different debug models can use the standard C breakpoints that + * extend the basic <code>ICBreakpoint</code>. The can use this extension + * mechanism to edit and store model-specific data in the original breakpoint + * object. + * + * A breakpoint extension is defined by an extension of kind + * <code>"org.eclipse.cdt.debug.core.BreakpointExtension"</code></li>. + * The <code>ICBreakpoint</code> implementation instantiates breakpoint + * extensions registered for its specific marker type when a client requests + * extensions for a given debug model type. Thus the extension classes and + * plugins that declare them are not loaded unless requested by a client. + * + * @see ICBreakpoint#getExtension(String, Class) + */ +public interface IDsfBreakpointExtension extends ICBreakpointExtension { + + public void setTargetFilter( IContainerDMContext target ) throws CoreException; + public void removeTargetFilter( IContainerDMContext target ) throws CoreException; + public IContainerDMContext[] getTargetFilters() throws CoreException; + + public void setThreadFilters( IExecutionDMContext[] threads ) throws CoreException; + public void removeThreadFilters( IExecutionDMContext[] threads ) throws CoreException; + public IExecutionDMContext[] getThreadFilters( IContainerDMContext target ) throws CoreException; + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IDsfDebugServicesFactory.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IDsfDebugServicesFactory.java new file mode 100644 index 00000000000..5b3aab48852 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IDsfDebugServicesFactory.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2008 Ericsson and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.service; + +import org.eclipse.cdt.dsf.service.DsfSession; + +/** + * A factory to create DSF services. Using this interface allows + * to easily have different service implementation for different backends. + * @since 1.1 + */ +public interface IDsfDebugServicesFactory { + <V> V createService(Class<V> clazz, DsfSession session, Object ... optionalArguments); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IExpressions.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IExpressions.java new file mode 100644 index 00000000000..adedef76125 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IExpressions.java @@ -0,0 +1,231 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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 + * Ericsson - Update for GDB/MI + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.service; + +import java.util.Map; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMData; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; + +/** + * Expressions service provides access to the debugger's expression evaluator. This service has + * dependencies on the Stack service, as it is be used to provide context for an + * expression to be evaluated. + */ +@SuppressWarnings("nls") +public interface IExpressions extends IFormattedValues { + + /** + * Expression context. + */ + public interface IExpressionDMContext extends IFormattedDataDMContext { + /** + * Returns a fully qualified expression string represented by this context. This + * expression string is the same as the string that is sent to the debug engine to be + * evaluated in context of a stack frame, thread, or a symbol context. + */ + String getExpression(); + } + + /** + * The address and size of an expression. + */ + public interface IExpressionDMAddress { + IAddress getAddress(); + int getSize(); + } + + /** + * This is the model data interface that corresponds to IExpressionDMContext. + */ + public interface IExpressionDMData extends IDMData { + // These static fields define the possible return values of method getTypeId(). + + final static String TYPEID_UNKNOWN = "TYPEID_UNKNOWN"; + final static String TYPEID_INTEGER = "TYPEID_INTEGER"; + final static String TYPEID_CHAR = "TYPEID_CHAR"; + final static String TYPEID_FLOAT = "TYPEID_FLOAT"; + final static String TYPEID_DOUBLE = "TYPEID_DOUBLE"; + final static String TYPEID_OPAQUE = "TYPEID_OPAQUE"; + + /** + * This enumerates the possible basic types that an expression can have. + * + * @see getBasicType(). + */ + enum BasicType { + unknown, // Unknown type. + basic, // Scalar type (e.g., int, short, float). + pointer, // Pointer to anything. + array, // Array of anything. + composite, // Struct, union, or class. + enumeration, // Enumeration. + function // Function. + } + + /** + * If this expression is a sub-expression of another expression, this method returns + * the expression relative to the parent of this expression. Otherwise this method + * will return the same string as {@link #getExpression()}. + */ + String getName(); + + /** + * @return A BasicType enumerator describing the basic type of an expression. + */ + BasicType getBasicType(); + + /** + * @return The source code type of this expression. This is a string such as "struct Foo", "short", + * "int *", "mytypedef", "(int *)[]", "enum Bar". If the debugger backend cannot supply + * this information, this method returns "<UNKNOWN>" (the angle brackets are there just in + * case there is a type named "UNKNOWN" in the application). + */ + String getTypeName(); + + /** + * This method needs to be defined. For now, this returns the empty string. + */ + String getEncoding(); + + /** + * @return One of the TYPEID_* static field values defined by this interface. + */ + String getTypeId(); + + /** + * @return A Map in which the keys are strings that are the names of enumerators in the enumeration + * that is the value of this expression and the values are the integer values of the + * enumerators. If the expression type is not an enumeration, this returns an empty Map. + */ + Map<String, Integer> getEnumerations(); + + /** + * This method needs to be defined. + */ + IRegisters.IRegisterDMContext getRegister(); + } + + /** + * Event indicating that a given expression is changed. If an expression is changed, it's implied that all + * the children of that expression are changed too. + */ + public interface IExpressionChangedDMEvent extends IDMEvent<IExpressionDMContext> {} + + /** + * Retrieves the expression DM data object for the given expression context(<tt>dmc</tt>). + * + * @param dmc + * The ExpressionDMC for the expression to be evaluated. + * @param rm + * The data request monitor that will contain the requested data + */ + void getExpressionData(IExpressionDMContext dmc, DataRequestMonitor<IExpressionDMData> rm); + + /** + * Retrieves the address and size of an expression given by the expression context(<tt>dmc</tt>). + * Non-lvalues do not have an addresses (e.g., "x + 5"). When the expression +- * has no address, the data request monitor will contain null. + * + * @param dmc + * The ExpressionDMC for the expression + * @param rm + * The data request monitor that will contain the requested data + */ + void getExpressionAddressData(IExpressionDMContext dmc, DataRequestMonitor<IExpressionDMAddress> rm); + + /** + * Returns the data model context object for the specified expression in the context + * specified by <b>ctx</b>. + * + * @param ctx: Context in which to evaluate the expression. This context could include the + * PC location, stack frame, thread, or just a symbol context. + * + * @param expression: The expression to evaluate. + * + * @return An expression data model context object that must be passed to + * getModelData() to obtain the value of the expression. + */ + IExpressionDMContext createExpression(IDMContext ctx, String expression); + + /** + * Retrieves the sub-expressions of the given expression. Sub-expressions are fields of a struct, union, + * or class, the enumerators of an enumeration, and the element of an array. + * + * @param exprCtx: The data model context representing an expression. + * + * @param rm: Request completion monitor containing an array of all sub-expressions + */ + void getSubExpressions(IExpressionDMContext exprCtx, DataRequestMonitor<IExpressionDMContext[]> rm); + + /** + * Retrieves a particular range of sub-expressions of the given expression. + * Sub-expressions are fields of a struct, union, or class, the enumerators + * of an enumeration, and the element of an array. + * + * @param exprCtx: The data model context representing an expression. + * startIndex: Index of the first sub-expression to retrieve + * length: Total number of sub-expressions to retrieve + * + * @param rm: Request completion monitor containing an array of the requested + * range of sub-expressions + */ + void getSubExpressions(IExpressionDMContext exprCtx, int startIndex, int length, + DataRequestMonitor<IExpressionDMContext[]> rm); + + /** + * Retrieves the number of sub-expressions of the given expression. Sub-expressions are fields of a struct, union, + * or class, the enumerators of an enumeration, and the element of an array. + * + * @param exprCtx: The data model context representing an expression. + * + * @param rm: Request completion monitor containing the number of sub-expressions + * of the specified expression + */ + void getSubExpressionCount(IExpressionDMContext exprCtx, DataRequestMonitor<Integer> rm); + + /** + * For object oriented languages, this method returns the expressions representing base types of + * the given expression type. + * + * @param exprContext: The data model context representing an expression. + * + * @param rm: Request completion monitor. + */ + void getBaseExpressions(IExpressionDMContext exprContext, DataRequestMonitor<IExpressionDMContext[]> rm); + + /** + * This method indicates if an expression can be written to. + * + * @param expressionContext: The data model context representing an expression. + * + * @param rm: Data Request monitor containing True if this expression's value can be edited. False otherwise. + */ + void canWriteExpression(IExpressionDMContext expressionContext, DataRequestMonitor<Boolean> rm); + + /** + * This method supports the writing/modifying the value of the expression. + * + * @param expressionContext: The data model context representing an expression. + * + * @param expressionValue: The new value of the expression as a String. + * + * @param formatId: The format ID specifying the format of parameter <b>expressionValue</b>. + * + * @param rm: Request completion monitor. + */ + void writeExpression(IExpressionDMContext expressionContext, String expressionValue, String formatId, RequestMonitor rm); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IFormattedValues.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IFormattedValues.java new file mode 100644 index 00000000000..5cc665aebb3 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IFormattedValues.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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.cdt.dsf.debug.service; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMData; +import org.eclipse.cdt.dsf.datamodel.IDMService; + +public interface IFormattedValues extends IDMService { + + /** Marker interface for a DMC that has a formatted value. */ + public interface IFormattedDataDMContext extends IDMContext {} + + /** + * These strings represent the standard known formats for any bit stream + * which needs to be formatted. These ID's as well as others which may be + * specifically available from the backend are what is returned from the + * getID() method. + */ + public final static String HEX_FORMAT = "HEX.Format" ; //$NON-NLS-1$ + public final static String OCTAL_FORMAT = "OCTAL.Format" ; //$NON-NLS-1$ + public final static String NATURAL_FORMAT = "NATURAL.Format" ; //$NON-NLS-1$ + public final static String BINARY_FORMAT = "BINARY.Format" ; //$NON-NLS-1$ + public final static String DECIMAL_FORMAT = "DECIMAL.Format" ; //$NON-NLS-1$ + public final static String STRING_FORMAT = "STRING.Format" ; //$NON-NLS-1$ + + /** + * Retrieves the formats that the given data is available in. + * This method is asynchronous because the service may need to retrieve + * information from the backend in order to determine what formats are + * available for the given data context. + * + * @param dmc Context for which to retrieve available formats. + * @param rm Completion monitor returns an array of support formatIds. + */ + public void getAvailableFormats(IFormattedDataDMContext dmc, DataRequestMonitor<String[]> rm); + + /** + * Creates a FormattedValueDMContext representing the given formatId. + * + * @param dmc Parent context for the context that is being created + * @param formatId Defines format to be used for the returned context. + */ + public FormattedValueDMContext getFormattedValueContext(IFormattedDataDMContext dmc, String formatId); + + /** + * Retrieves the DM data associated with given formatted value context. + * @param dmc Context to retrieve the value for. + * @param rm Completion monitor returns the formatted value. + */ + public void getFormattedExpressionValue(FormattedValueDMContext dmc, DataRequestMonitor<FormattedValueDMData> rm); + + + /** + * DMC that represents a value with specific format. The format ID can be + * persisted and used for comparison. + */ + + public static class FormattedValueDMContext extends AbstractDMContext + { + private final String fFormatID; + + public FormattedValueDMContext(IDMService service, IDMContext parent, String formatId) { + super(service, new IDMContext[] { parent }); + fFormatID = formatId; + } + + public FormattedValueDMContext(String sessionId, IDMContext parent, String formatId) { + super(sessionId, new IDMContext[] { parent }); + fFormatID = formatId; + } + + public String getFormatID() { + return fFormatID; + } + + @Override + public boolean equals(Object obj) { + return baseEquals(obj) && ((FormattedValueDMContext)obj).getFormatID().equals(getFormatID()); + } + + @Override + public int hashCode() { + return baseHashCode() + getFormatID().hashCode(); + } + + @Override + public String toString() { + return baseToString() + ".format(" + getFormatID() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + public static class FormattedValueDMData implements IDMData { + + private final String fValue; + + public FormattedValueDMData(String value) { + fValue = value; + } + + public String getFormattedValue() { + return fValue; + } + + + } +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IInstruction.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IInstruction.java new file mode 100644 index 00000000000..dd547ff5746 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IInstruction.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2008 Ericsson and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ericsson - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.service; + +import java.math.BigInteger; + +/** + * Represents an assembly instruction + */ +public interface IInstruction { + + /** + * @return the instruction address. + */ + BigInteger getAdress(); + + /** + * @return the function name. + */ + String getFuntionName(); + + /** + * @return the offset of this machine instruction + */ + long getOffset(); + + /** + * @return the instruction. + */ + String getInstruction(); + + /** + * @return the opcode + */ + String getOpcode(); + + /** + * @return any arguments to the instruction + */ + String getArgs(); + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IMemory.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IMemory.java new file mode 100644 index 00000000000..a8444301527 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IMemory.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 Wind River Systems 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 + * Ericsson AB - extended the API for IMemoryBlockExtension + * Ericsson AB - added support for 64 bit processors + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.service; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.service.IDsfService; +import org.eclipse.debug.core.model.MemoryByte; + +/** + * Service for accessing memory. Memory contexts are not meant to be + * represented in tree or table views, so it doesn't need to implement + * IDMService interface. + */ +public interface IMemory extends IDsfService { + + public interface IMemoryDMContext extends IDMContext {} + + /** + * Event generated every time a range of bytes is modified. + * + * A client wishing to receive such events has to register as a service + * event listener and implement the corresponding eventDispatched method. + * + * E.g.: + * + * MyMemoryBlock(MIRunControl fRunControl) + * { + * ... + * fRunControl.getSession().addServiceEventListener(MyMemoryBlock.this, null); + * ... + * } + * + * @DsfServiceEventHandler + * public void eventDispatched(MemoryChangedEvent e) { + * IDMContext<?> context = e.getContext(); + * IAddress[] addresses = e.getAddresses(); + * // do whatever... + * } + */ + public interface IMemoryChangedEvent extends IDMEvent<IMemoryDMContext> { + IAddress[] getAddresses(); + } + + /** + * Reads a memory block from the target. + * + * An asynchronous memory read request at [address] + [offset] for + * [count] memory items, each of size [word_size] bytes, will be + * issued to the target. The result will be stored in [drm] upon + * completion of the call. + * + * The [drm] result buffer will be of size [word_size] * [count]. The + * successfully read bytes will have their MemoryByte.READABLE flag + * set while the bytes in error (unreachable/bad memory) will have their + * flag byte set to 0. The bytes will respect the target "endianness". + * + * @param context the context of the target memory block + * @param address the memory block address (on the target) + * @param offset the offset from the start address + * @param word_size the size, in bytes, of an addressable item + * @param count the number of data elements to read + * @param drm the asynchronous data request monitor + */ + public void getMemory(IMemoryDMContext context, IAddress address, long offset, + int word_size, int count, DataRequestMonitor<MemoryByte[]> drm); + + /** + * Writes a memory block on the target. + * + * An asynchronous memory write request at [address] + [offset] for + * [count] * [word_size] bytes will be issued to the target. + * + * The [buffer] must hold at least [count] * [word_size] bytes. + * + * A MemoryChangedEvent will be generated for the range of addresses. + * + * @param context the context of the target memory block + * @param address the memory block address (on the target) + * @param offset the offset from the start address + * @param word_size the size, in bytes, of an addressable item + * @param count the number of data elements to write + * @param buffer the source buffer + * @param rm the asynchronous data request monitor + */ + public void setMemory(IMemoryDMContext context, IAddress address, long offset, + int word_size, int count, byte[] buffer, RequestMonitor rm); + + /** + * Writes [pattern] at memory [address] + [offset], [count] times. + * + * A MemoryChangedEvent will be generated for the range of addresses. + * + * @param context the context of the target memory block + * @param address the memory block address (on the target) + * @param offset the offset from the start address + * @param word_size the size, in bytes, of an addressable item + * @param count the number of times [pattern] will be written + * @param pattern the source buffer + * @param rm the asynchronous data request monitor + */ + public void fillMemory(IMemoryDMContext context, IAddress address, long offset, + int word_size, int count, byte[] pattern, RequestMonitor rm); + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IMixedInstruction.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IMixedInstruction.java new file mode 100644 index 00000000000..511f81f1785 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IMixedInstruction.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2008 Ericsson and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ericsson - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.service; + +/** + * Represents the assembly instruction(s) corresponding to a source line + */ +public interface IMixedInstruction { + + /** + * @return the file name + */ + String getFileName(); + + /** + * @return the line Number. + */ + int getLineNumber(); + + /** + * @return the array of instruction. + */ + IInstruction[] getInstructions(); + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IModules.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IModules.java new file mode 100644 index 00000000000..c01ea5db89a --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IModules.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.dsf.debug.service; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.service.IDsfService; + +/** + * Debugger service representing module handling logic of a debugger. + */ +public interface IModules extends IDsfService { + + /** + * Symbol context represents the space into which module symbols are loaded. + * Traditionally symbols are loaded in context of a process, but for other + * types of debugging, like kernel or no-OS debugging, it's useful to + * separate the concept of a symbol context from a process. + */ + public interface ISymbolDMContext extends IDMContext {} + + /** + * Module context represents a single module that is loaded. + */ + public interface IModuleDMContext extends IDMContext {} + + /** + * Event indicating a change in the symbol information for given context. + */ + public interface ModulesChangedDMEvent extends IDMEvent<ISymbolDMContext> {} + + /** + * Specific event identifying that a new module was loaded into a + * symbol context. + */ + public interface ModuleLoadedDMEvent extends ModulesChangedDMEvent { + /** Returns context of the module that was loaded */ + IModuleDMContext getLoadedModuleContext(); + } + + public interface ModuleUnloadedDMEvent extends ModulesChangedDMEvent { + /** Returns context of the module that was un-loaded */ + IModuleDMContext getUnloadedModuleContext(); + } + + /** Module information. */ + public interface IModuleDMData { + String getName(); + String getFile(); + long getTimeStamp(); + String getBaseAddress(); + String getToAddress(); + boolean isSymbolsLoaded(); + long getSize(); + } + + /** Line information about a particular address */ + public interface LineInfo { + IAddress getAddress(); + String getSourceFile(); + int getStartLine(); + int getStartColumn(); + int getEndLine(); + int getEndColumn(); + } + + /** Address information about a particular file/line */ + public interface AddressRange { + IAddress getStartAddress(); + IAddress getEndAddress(); + } + + void getModuleData(IModuleDMContext dmc, DataRequestMonitor<IModuleDMData> rm); + + /** + * Retreives the list of modules loaded in given symbol context. + */ + void getModules(ISymbolDMContext symCtx, DataRequestMonitor<IModuleDMContext[]> rm); + + /** + * Calculates the line numbers corresponding to the given address. + */ + void calcLineInfo(ISymbolDMContext symCtx, IAddress address, DataRequestMonitor<LineInfo[]> rm); + + /** + * Calculates the addresses corresponding to the given source file location. + */ + void calcAddressInfo(ISymbolDMContext symCtx, String file, int line, int col, DataRequestMonitor<AddressRange[]> rm); + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IProcesses.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IProcesses.java new file mode 100644 index 00000000000..db65c2c89ed --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IProcesses.java @@ -0,0 +1,181 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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 + * Ericsson - Updated for latest DSF version + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.service; + +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMData; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.datamodel.IDMService; + +/** + * This interface provides access to the OS's process + * information, manipulation methods, and debugging methods. + * This service provides a relatively simple interface for + * manipulating processes as compared with a full-blown + * remote target debugger. + * @since 1.1 + */ +public interface IProcesses extends IDMService { + + /** + * A thread as known by the OS. + * This context is kept different than {@link IRunControl.IExecutionDMContext} + * because the OS id of a thread may not be the same as the thread id used by + * the debugger when doing run control operations. + */ + public interface IThreadDMContext extends IDMContext {} + + /** + * A process as known by the OS. + * This context is kept different than {@link IRunControl.IContainerDMContext} + * because the OS id of a process may not be the same as the process id used by + * the debugger when doing run control operations. + */ + public interface IProcessDMContext extends IThreadDMContext {} + + /** + * Interface for thread and process object data. This data provides a link + * to the lower level debugger services, in form of execution contexts. + */ + public interface IThreadDMData extends IDMData { + String getName(); + String getId(); + boolean isDebuggerAttached(); + } + + /** + * Event indicating that process data has changed. + */ + public interface ProcessChangedDMEvent extends IDMEvent<IProcessDMContext> {} + + /** + * Retrieves thread or process data for given context. + * @param dmc Context to retrieve data for. + * @param rm Request completion monitor. + */ + public void getExecutionData(IThreadDMContext dmc, DataRequestMonitor<IThreadDMData> rm); + + /** + * Retrieves the debugging context that characterizes the specified thread + * or process context. + * + * @param dmc The thread or process dmc for which we want the debugging context + * @param rm The request monitor that will contain the debugging context. + * null if no such context exists + */ + public void getDebuggingContext(IThreadDMContext dmc, DataRequestMonitor<IDMContext> rm); + + /** + * Retrieves the current list of processes running on target. + * @param dmc The processor or core for which to list all processes + * @param rm Request completion monitor, to be filled in with array of process contexts. + */ + void getRunningProcesses(IDMContext dmc, DataRequestMonitor<IProcessDMContext[]> rm); + + /** + * Checks whether it is possible to attach the debugger to a new process. + * @param dmc The processor or core on which we want to attach to a process. + * @param rm Return if it is possible to attach. + */ + void isDebuggerAttachSupported(IDMContext dmc, DataRequestMonitor<Boolean> rm); + + /** + * Attaches debugger to the given process. + * When attaching to a process, a debugging context can now be used to characterize the process. + * This method can optionally choose to return this IDMContext inside the DataRequestMonitor. + * This can be useful for backends that do not have the ability to obtain the different + * debugging IDMContexts through {@link #getProcessesBeingDebugged(IDMContext, DataRequestMonitor) + */ + void attachDebuggerToProcess(IProcessDMContext procCtx, DataRequestMonitor<IDMContext> rm); + + /** + * Checks whether it is possible to detach the debugger from the specified process. + * @param dmc The debugging context from which we want to detach. This context + * should have IProcessDMContext as an ancestor. + * @param rm Return if it is possible to detach. + */ + void canDetachDebuggerFromProcess(IDMContext dmc, DataRequestMonitor<Boolean> rm); + + /** + * Detaches debugger from the given process. + * @param dmc The debugging context from which we want to detach. This context + * should have IProcessDMContext as an ancestor. + */ + void detachDebuggerFromProcess(IDMContext dmc, RequestMonitor requestMonitor); + + /** + * Checks whether it is possible to run a new process. + * @param dmc The processor or core on which we want to run a new process. + * @param rm Return if it is possible to run a new process. + */ + void isRunNewProcessSupported(IDMContext dmc, DataRequestMonitor<Boolean> rm); + + /** + * Starts a new process. + * @param dmc The processor or core on which we want to run a new process. + * @param file Process image to use for the new process. + * @param attributes Attributes that give information on the process to be debugged + * @param rm Request completion monitor, to be filled in with the process context. + */ + void runNewProcess(IDMContext dmc, + String file, + Map<String, Object> attributes, + DataRequestMonitor<IProcessDMContext> rm); + + /** + * Checks whether it is possible to start a new process with the debugger attached + * @param dmc The processor or core on which we want to start and debug the new process. + * @param rm Return if it is possible to start a new process with the debugger attached. + */ + void isDebugNewProcessSupported(IDMContext dmc, DataRequestMonitor<Boolean> rm); + + /** + * Starts a new process with the debugger attached. + * @param dmc The processor or core on which we want to start and debug the new process. + * @param file Process image to use for the new process. + * @param attributes Attributes that give information on the process to be debugged + * @param rm Request completion monitor, to be filled in with the + * debugging context that can now be used to characterize the process + */ + void debugNewProcess(IDMContext dmc, + String file, + Map<String, Object> attributes, + DataRequestMonitor<IDMContext> rm); + + /** + * Retrieves the list of processes which are currently under debugger control. + * + * @param dmc The processor or core for which to list processes being debugged + * @param rm Request completion monitor which contains all debugging contexts representing + * the processes being debugged. Note that each of these contexts should also have + * IProcessDMContext as a parent. + */ + void getProcessesBeingDebugged(IDMContext dmc, DataRequestMonitor<IDMContext[]> rm); + + /** + * Checks whether the given process or thread can be terminated. + * @param thread Thread or process to terminate. + * @param rm Return token. + */ + void canTerminate(IThreadDMContext thread, DataRequestMonitor<Boolean> rm); + + /** + * Terminates the selected process or thread. + * @param thread Thread or process to terminate. + * @param rm Request completion monitor, indicates success or failure. + */ + void terminate(IThreadDMContext thread, RequestMonitor requestMonitor); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IRegisters.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IRegisters.java new file mode 100644 index 00000000000..7397ead772b --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IRegisters.java @@ -0,0 +1,208 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.dsf.debug.service; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMData; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; + +/** + * Service for accessing register data. + */ +public interface IRegisters extends IFormattedValues { + + /** + * Event indicating groups have changed. The type of context returned by this + * event is generic, because different implementations of the the register service + * could configure register groups using different contexts. Some implementations + * could configure different register groups for each execution context, other + * services may have a global list of groups. + */ + public interface IGroupsChangedDMEvent extends IDMEvent<IDMContext> {} + + /** Register group context */ + public interface IRegisterGroupDMContext extends IFormattedDataDMContext { + } + + /** Event indicating values for the group have changed. */ + public interface IGroupChangedDMEvent extends IDMEvent<IRegisterGroupDMContext> {} + + /** Event indicating registers in a group have changed. */ + public interface IRegistersChangedDMEvent extends IDMEvent<IRegisterGroupDMContext> {} + + /** + * Register groups only have a name and description. Sub groups and registers are + * retrieved through the service interface. + */ + public interface IRegisterGroupDMData extends IDMData { + public String getName(); + public String getDescription(); + } + + /** Register context */ + public interface IRegisterDMContext extends IFormattedDataDMContext { + } + + /** Event indicating register value changed. */ + public interface IRegisterChangedDMEvent extends IDMEvent<IRegisterDMContext> {} + + /** Register information */ + public interface IRegisterDMData extends IDMData { + String getName(); + String getDescription(); + boolean isReadable(); + boolean isReadOnce(); + boolean isWriteable(); + boolean isWriteOnce(); + boolean hasSideEffects(); + boolean isVolatile(); + boolean isFloat(); + } + + /** Bit field context */ + public interface IBitFieldDMContext extends IFormattedDataDMContext { + } + + /** Event indicating register value changed. */ + public interface IBitFieldChangedDMEvent extends IDMEvent<IBitFieldDMContext> {} + + /** + * Bitfield data, big groups and mnemonics are retrieved at the same + * time as rest of bit field data + */ + public interface IBitFieldDMData extends IDMData { + String getName(); + String getDescription(); + boolean isReadable(); + boolean isReadOnce(); + boolean isWriteable(); + boolean isWriteOnce(); + boolean hasSideEffects(); + boolean isZeroBasedNumbering(); + boolean isZeroBitLeftMost(); + IBitGroup[] getBitGroup(); + IMnemonic[] getMnemonics(); + IMnemonic getCurrentMnemonicValue(); + } + + /** Bit group definition */ + public interface IBitGroup { + int startBit(); + int bitCount(); + } + + /** Bit field mnemonic */ + public interface IMnemonic { + String getShortName(); + String getLongName(); + } + + /** + * Retrieves the list of register groups. + * @param ctx Context for the returned data. + * @param rm Request completion monitor. + */ + void getRegisterGroups(IDMContext ctx, DataRequestMonitor<IRegisterGroupDMContext[]> rm); + + /** + * Retrieves the list of registers for the given context. The given context could include + * a register group and an execution context or just an execution context, in which case all + * registers for all groups should be returned. + * @param ctx Context for the returned data. + * @param rm Request completion monitor. + */ + void getRegisters(IDMContext ctx, DataRequestMonitor<IRegisterDMContext[]> rm); + + /** + * Retrieves bit fields for given register + * @param ctx Context for the returned data. + * @param rm Request completion monitor. + */ + void getBitFields(IDMContext ctx, DataRequestMonitor<IBitFieldDMContext[]> rm); + + /** + * Retrieves a Register Group context. The given context could include a register + * group and an execution context or just an execution context. + * @param ctx Context for the returned data. + * @param name Name of group being requested + * @param rm Request completion monitor. + */ + void findRegisterGroup(IDMContext ctx, String name, DataRequestMonitor<IRegisterGroupDMContext> rm); + + /** + * Retrieves a Register context. The given context could include a register group and an execution + * context or just an execution context. + * @param ctx Context for the returned data. + * @param name Name of register being requested + * @param rm Request completion monitor. + */ + void findRegister(IDMContext ctx, String name, DataRequestMonitor<IRegisterDMContext> rm); + + /** + * Retrieves bit field context. The given context could include a register and an execution + * context or just an execution context. + * @param ctx Context for the returned data. + * @param name Name of bit field being requested + * @param rm Request completion monitor. + */ + void findBitField(IDMContext ctx, String name, DataRequestMonitor<IBitFieldDMContext> rm); + + /** + * Retrieves register group data for given context. + * @param dmc Context to retrieve data for. + * @param rm Request completion monitor. + */ + void getRegisterGroupData(IRegisterGroupDMContext dmc, DataRequestMonitor<IRegisterGroupDMData> rm); + + /** + * Retrieves register data for given context. + * @param dmc Context to retrieve data for. + * @param rm Request completion monitor. + */ + void getRegisterData(IRegisterDMContext dmc , DataRequestMonitor<IRegisterDMData> rm); + + /** + * Retrieves bit field data for given context. + * @param dmc Context to retrieve data for. + * @param rm Request completion monitor. + */ + void getBitFieldData(IBitFieldDMContext dmc , DataRequestMonitor<IBitFieldDMData> rm); + + + + /** + * Writes a register value for a given register to the target + * @param regCtx Context containing the register. + * @param regValue Value of the register to be written. + * @param formatId Format of the value to be written. + * @param rm Request completion monitor. + */ + void writeRegister(IRegisterDMContext regCtx, String regValue, String formatId, RequestMonitor rm); + + /** + * Writes a bit field value for a given bit field to the target + * @param bitFieldCtx Context containing the bit field. + * @param bitFieldValue Value of the bit field to be written. + * @param formatId Format of the value to be written. + * @param rm Request completion monitor. + */ + void writeBitField(IBitFieldDMContext bitFieldCtx, String bitFieldValue, String formatId, RequestMonitor rm); + + /** + * Writes a bit field value for a given bit field to the target + * @param bitFieldCtx Context containing the bit field. + * @param mnemonic Mnemonic which represents the value to be written. + * @param rm Request completion monitor. + */ + void writeBitField(IBitFieldDMContext bitFieldCtx, IMnemonic mnemonic, RequestMonitor rm); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IRunControl.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IRunControl.java new file mode 100644 index 00000000000..181d2f44418 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IRunControl.java @@ -0,0 +1,127 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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 + * Ericsson - Modified for additional functionality + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.service; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMData; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.datamodel.IDMService; + +/** + * This interface provides access to controlling and monitoring the execution + * state of a process being debugged. This interface does not actually + * provide methods for creating or destroying execution contexts, it doesn't + * even have methods for getting labels. That's because it is expected that + * higher level services, ones that deal with processes, kernels, or target + * features will provide that functionality. + */ +public interface IRunControl extends IDMService +{ + /** + * Execution context is the object on which run control operations can be + * performed. A lot of higher-level services reference this context to build + * functionality on top of it, e.g. stack, expression evaluation, registers, etc. + */ + public interface IExecutionDMContext extends IDMContext {} + + /** + * Context representing a process, kernel, or some other logical container + * for execution contexts, which by itself can perform run-control + * operations. + */ + + public interface IContainerDMContext extends IExecutionDMContext {} + + /** Flag indicating reason context state change. */ + public enum StateChangeReason { UNKNOWN, USER_REQUEST, STEP, BREAKPOINT, EXCEPTION, CONTAINER, WATCHPOINT, SIGNAL, SHAREDLIB, ERROR, EVALUATION }; + + /** + * Indicates that the given thread has suspended. + */ + public interface ISuspendedDMEvent extends IDMEvent<IExecutionDMContext> { + StateChangeReason getReason(); + } + + /** + * Indicates that the given thread has resumed. + */ + public interface IResumedDMEvent extends IDMEvent<IExecutionDMContext> { + StateChangeReason getReason(); + } + + /** + * Indicates that the given container has suspended. + */ + public interface IContainerSuspendedDMEvent extends ISuspendedDMEvent { + /** + * Returns the contexts which triggered the resume, which could be + * an empty array if not known. + */ + IExecutionDMContext[] getTriggeringContexts(); + } + + /** + * Indicates that the given container has resumed. + */ + public interface IContainerResumedDMEvent extends IResumedDMEvent { + /** + * Returns the contexts which triggered the resume, which could be an + * empty array if not known. + */ + IExecutionDMContext[] getTriggeringContexts(); + } + + /** + * Indicates that a new execution context was started. + */ + public interface IStartedDMEvent extends IDMEvent<IExecutionDMContext> {} + + /** + * Indicates that an execution context has exited. + */ + public interface IExitedDMEvent extends IDMEvent<IExecutionDMContext> {} + + /** + * Display information for an execution context. + */ + public interface IExecutionDMData extends IDMData { + StateChangeReason getStateChangeReason(); + } + + /** + * Retrieves execution data for given context. + * @param dmc Context to retrieve data for. + * @param rm Request completion monitor. + */ + public void getExecutionData(IExecutionDMContext dmc, DataRequestMonitor<IExecutionDMData> rm); + + /** + * Returns execution contexts belonging to the given container context. + */ + public void getExecutionContexts(IContainerDMContext c, DataRequestMonitor<IExecutionDMContext[]> rm); + + /* + * Run control commands. They all require the IExecutionContext object on + * which they perform the operations. + */ + void canResume(IExecutionDMContext context, DataRequestMonitor<Boolean> rm); + void canSuspend(IExecutionDMContext context, DataRequestMonitor<Boolean> rm); + boolean isSuspended(IExecutionDMContext context); + void resume(IExecutionDMContext context, RequestMonitor requestMonitor); + void suspend(IExecutionDMContext context, RequestMonitor requestMonitor); + public enum StepType { STEP_OVER, STEP_INTO, STEP_RETURN, INSTRUCTION_STEP_OVER, INSTRUCTION_STEP_INTO, INSTRUCTION_STEP_RETUTRN }; + boolean isStepping(IExecutionDMContext context); + void canStep(IExecutionDMContext context, StepType stepType, DataRequestMonitor<Boolean> rm); + void step(IExecutionDMContext context, StepType stepType, RequestMonitor requestMonitor); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ISignals.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ISignals.java new file mode 100644 index 00000000000..3fba2d7da39 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ISignals.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems 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.cdt.dsf.debug.service; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.service.IDsfService; + +/** + * + */ +public interface ISignals extends IDsfService { + /** + * Marker interface for a context for which signals can be set. + */ + public interface ISignalsDMContext extends IDMContext {}; + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ISourceLookup.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ISourceLookup.java new file mode 100644 index 00000000000..b92dd3c83b1 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ISourceLookup.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 Wind River Systems 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.cdt.dsf.debug.service; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.service.IDsfService; + +/** + * Service for mapping debugger paths to host paths. This service is needed + * primarily by other services that need to access source-path mappings, such + * as the breakpoints service. For UI components, the platform source lookup + * interfaces could be sufficient. + */ +public interface ISourceLookup extends IDsfService { + + public interface ISourceLookupDMContext extends IDMContext {} + + public interface ISourceLookupChangedDMEvent extends IDMEvent<ISourceLookupDMContext> {} + + /** + * Retrieves the host source object for given debugger path string. + */ + void getSource(ISourceLookupDMContext ctx, String debuggerPath, DataRequestMonitor<Object> rm); + + /** + * Retrieves the debugger path string for given host source object. + */ + void getDebuggerPath(ISourceLookupDMContext ctx, Object source, DataRequestMonitor<String> rm); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IStack.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IStack.java new file mode 100644 index 00000000000..d6003483f94 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IStack.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2006,, 2008 Wind River Systems 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.cdt.dsf.debug.service; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMData; +import org.eclipse.cdt.dsf.datamodel.IDMService; + +/** + * Stack service provides access to stack information for a + * given execution context. + */ +public interface IStack extends IDMService { + + /** + * Context for a specific stack frame. Besides allowing access to stack + * frame data, this context is used by other services that require a stack + * frame for evaluation. + */ + public interface IFrameDMContext extends IDMContext { + int getLevel(); + } + + /** + * Stack frame information. + */ + public interface IFrameDMData extends IDMData { + IAddress getAddress(); + String getFile(); + String getFunction(); + int getLine(); + int getColumn(); + } + + /** + * Variable context. This context only provides access to limited + * expression information. For displaying complete information, + * Expressions service should be used. + */ + public interface IVariableDMContext extends IDMContext {} + + /** + * Stack frame variable information. + */ + public interface IVariableDMData extends IDMData { + String getName(); + String getValue(); + } + + /** + * Retrieves stack frame data for given context. + * @param frameDmc Context to retrieve data for. + * @param rm Request completion monitor. + */ + public void getFrameData(final IFrameDMContext frameDmc, DataRequestMonitor<IFrameDMData> rm); + + /** + * Retrieves stack frame variable data for given context. + * @param variableDmc Context to retrieve data for. + * @param rm Request completion monitor. + */ + public void getVariableData(IVariableDMContext variableDmc, DataRequestMonitor<IVariableDMData> rm); + + /** + * Retrieves list of stack frames for the given execution context. Request + * will fail if the stack frame data is not available. + */ + void getFrames(IDMContext execContext, DataRequestMonitor<IFrameDMContext[]> rm); + + /** + * Retrieves the top stack frame for the given execution context. + * Retrieving just the top frame DMC and corresponding data can be much + * more efficient than just retrieving the whole stack, before the data + * is often included in the stopped event. Also for some UI functionality, + * such as setpping, only top stack frame is often needed. + * @param execContext + * @param rm + */ + void getTopFrame(IDMContext execContext, DataRequestMonitor<IFrameDMContext> rm); + + /** + * Retrieves variables which were arguments to the stack frame's function. + */ + void getArguments(IFrameDMContext frameCtx, DataRequestMonitor<IVariableDMContext[]> rm); + + /** + * Retrieves variables local to the stack frame, including arguments. + */ + void getLocals(IFrameDMContext frameCtx, DataRequestMonitor<IVariableDMContext[]> rm); + + /** + * Retrieves the number of stack frames available for the given context.. + * @param dmc Context to retrieve data for. + * @param The maximum depth of stack to calculate. Should be 0 to calculate + * depth with no limit. + * @param rm Callback + */ + void getStackDepth(IDMContext dmc, int maxDepth, DataRequestMonitor<Integer> rm); + + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IStack2.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IStack2.java new file mode 100644 index 00000000000..ae2f507839f --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IStack2.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.service; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; + +/** + * Stack service extension. + * <p> + * Adds the capability to retrieve a limited number of stack frames. + * </p> + * + * @since DSF 1.1 + */ +public interface IStack2 extends IStack { + + /** + * Convenience constant for use with {@link #getFrames(IDMContext, int, int, DataRequestMonitor)} + * to retrieve all stack frames. + */ + public final static int ALL_FRAMES = -1; + + /** + * Retrieves list of stack frames for the given execution context. Request + * will fail if the stack frame data is not available. + * <p>The range of stack frames can be limited by the <code>startIndex</code> and <code>endIndex</code> arguments. + * It is no error to specify an <code>endIndex</code> exceeding the number of available stack frames. + * A negative value for <code>endIndex</code> means to retrieve all stack frames. <code>startIndex</code> must be a non-negative value. + * </p> + * + * @param execContext the execution context to retrieve stack frames for + * @param startIndex the index of the first frame to retrieve + * @param endIndex the index of the last frame to retrieve (inclusive) or {@link #ALL_FRAMES} + * @param rm the request monitor + * + * @see #getFrames(IDMContext, DataRequestMonitor) + */ + public abstract void getFrames(IDMContext execContext, int startIndex, int endIndex, DataRequestMonitor<IFrameDMContext[]> rm); + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IStepQueueManager.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IStepQueueManager.java new file mode 100644 index 00000000000..16167692125 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IStepQueueManager.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.service; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StepType; +import org.eclipse.cdt.dsf.service.DsfSession; + +/** + * @since 1.1 + */ +@ConfinedToDsfExecutor("getSession().getExecutor()") +public interface IStepQueueManager { + + /** + * Returns the session for which this step queue manager is used. + */ + public DsfSession getSession(); + + /** + * Checks whether a step command can be queued up for given context. + */ + public abstract void canEnqueueStep(IExecutionDMContext execCtx, StepType stepType, DataRequestMonitor<Boolean> rm); + + /** + * Adds a step command to the execution queue for given context. + * @param execCtx Execution context that should perform the step. + * @param stepType Type of step to execute. + */ + public abstract void enqueueStep(final IExecutionDMContext execCtx, final StepType stepType); + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ISymbols.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ISymbols.java new file mode 100644 index 00000000000..bf92dd13caa --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/ISymbols.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.dsf.debug.service; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMData; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.datamodel.IDMService; + +/** + * Service for accessing debugger symbols. This service builds on the Modules + * service, but not all debuggers provide access for parsing symbols so this + * service is separated. + * @see IModules + */ +public interface ISymbols extends IDMService { + public interface ISymbolObjectDMContext extends IDMContext {} + + /** + * Data about a debug symbol. + */ + public interface ISymbolObjectDMData extends IDMData { + String getName(); + String getTypeName(); + String getFilepath(); + } + + /** + * Indicates that the list of symbol objects is changed. Parsing debug + * symbols can be a long running operation (order of 10's of seconds or + * minues), so it is useful for the service to provide access to the data + * even while it's still parsing. This event may be issued periodically + * by the service to indicate that a section of debug symbols has been + * parsed. + */ + public interface ISymbolDataChangedDMEvent extends IDMEvent<IModules.ISymbolDMContext> {} + + /** + * Retrieves the list of symbols. + * @param symCtx Symbols context to retrieve symbols for. + * @param rm Request completion monitor. The return value is an iterator (rather than + * array) since there could be a very large number of symbols returned. + */ + public void getSymbols(IModules.ISymbolDMContext symCtx, DataRequestMonitor<Iterable<ISymbolObjectDMContext>> rm); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/StepQueueManager.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/StepQueueManager.java new file mode 100644 index 00000000000..3054e2af136 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/StepQueueManager.java @@ -0,0 +1,248 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.dsf.debug.service; + +import java.util.HashMap; +import java.util.Hashtable; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StepType; +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.osgi.framework.BundleContext; + +/** + * This service builds on top of standard run control service to provide + * step queuing functionality. Step queuing essentially allows user to press + * and hold the step key and achieve maximum stepping speed. If this service + * is used, other service implementations, such as stack and expressions, can + * use it to avoid requesting data from debugger back end if another step is + * about to be executed. + * + * @deprecated The functionality has been integrated in the UI layer. + */ +@Deprecated +public class StepQueueManager extends AbstractDsfService implements IStepQueueManager +{ + /** + * Amount of time in milliseconds, that it takes the ISteppingTimedOutEvent + * event to be issued after a step is started. + * @see ISteppingTimedOutEvent + */ + public final static int STEPPING_TIMEOUT = 500; + + /** + * The depth of the step queue. In other words, the maximum number of steps + * that are queued before the step queue manager throwing them away. + */ + public final static int STEP_QUEUE_DEPTH = 3; + + /** + * Indicates that the given context has been stepping for some time, + * and the UI (views and actions) may need to be updated accordingly. + */ + public interface ISteppingTimedOutEvent extends IDMEvent<IExecutionDMContext> {} + + + private static class StepRequest { + StepType fStepType; + StepRequest(StepType type) { + fStepType = type; + } + } + + private IRunControl fRunControl; + private int fQueueDepth = STEP_QUEUE_DEPTH; + private Map<IExecutionDMContext,List<StepRequest>> fStepQueues = new HashMap<IExecutionDMContext,List<StepRequest>>(); + private Map<IExecutionDMContext,Boolean> fTimedOutFlags = new HashMap<IExecutionDMContext,Boolean>(); + private Map<IExecutionDMContext,ScheduledFuture<?>> fTimedOutFutures = new HashMap<IExecutionDMContext,ScheduledFuture<?>>(); + + public StepQueueManager(DsfSession session) { + super(session); + } + + /////////////////////////////////////////////////////////////////////////// + // IDsfService + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize( + new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + doInitialize(requestMonitor); + }}); + } + + private void doInitialize(final RequestMonitor requestMonitor) { + fRunControl = getServicesTracker().getService(IRunControl.class); + + getSession().addServiceEventListener(this, null); + register(new String[]{ StepQueueManager.class.getName()}, new Hashtable<String,String>()); + requestMonitor.done(); + } + + @Override + public void shutdown(final RequestMonitor requestMonitor) { + unregister(); + getSession().removeServiceEventListener(this); + super.shutdown(requestMonitor); + } + + @Override + protected BundleContext getBundleContext() { + return DsfPlugin.getBundleContext(); + } + + /* + * @see org.eclipse.cdt.dsf.debug.service.IStepQueueManager#canEnqueueStep(org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext, org.eclipse.cdt.dsf.debug.service.IRunControl.StepType, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) + */ + public void canEnqueueStep(IExecutionDMContext execCtx, StepType stepType, DataRequestMonitor<Boolean> rm) { + if (doCanEnqueueStep(execCtx, stepType)) { + rm.setData(true); + rm.done(); + } else { + fRunControl.canStep(execCtx, stepType, rm); + } + } + + private boolean doCanEnqueueStep(IExecutionDMContext execCtx, StepType stepType) { + return fRunControl.isStepping(execCtx) && !isSteppingTimedOut(execCtx); + } + + /** + * Returns the number of step commands that are queued for given execution + * context. + */ + public int getPendingStepCount(IExecutionDMContext execCtx) { + List<StepRequest> stepQueue = fStepQueues.get(execCtx); + if (stepQueue == null) return 0; + return stepQueue.size(); + } + + /* + * @see org.eclipse.cdt.dsf.debug.service.IStepQueueManager#enqueueStep(org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext, org.eclipse.cdt.dsf.debug.service.IRunControl.StepType) + */ + public void enqueueStep(final IExecutionDMContext execCtx, final StepType stepType) { + fRunControl.canStep( + execCtx, stepType, new DataRequestMonitor<Boolean>(getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess() && getData()) { + fRunControl.step(execCtx, stepType, new RequestMonitor(getExecutor(), null)); + } else if (doCanEnqueueStep(execCtx, stepType)) { + List<StepRequest> stepQueue = fStepQueues.get(execCtx); + if (stepQueue == null) { + stepQueue = new LinkedList<StepRequest>(); + fStepQueues.put(execCtx, stepQueue); + } + if (stepQueue.size() < fQueueDepth) { + stepQueue.add(new StepRequest(stepType)); + } + } + } + }); + } + + /** + * Returns whether the step instruction for the given context has timed out. + */ + public boolean isSteppingTimedOut(IExecutionDMContext execCtx) { + for (IExecutionDMContext timedOutCtx : fTimedOutFlags.keySet()) { + if (execCtx.equals(timedOutCtx) || DMContexts.isAncestorOf(execCtx, timedOutCtx)) { + return fTimedOutFlags.get(timedOutCtx); + } + } + return false; + } + + + /////////////////////////////////////////////////////////////////////////// + + @DsfServiceEventHandler + public void eventDispatched(final ISuspendedDMEvent e) { + // Take care of the stepping time out + fTimedOutFlags.remove(e.getDMContext()); + ScheduledFuture<?> future = fTimedOutFutures.remove(e.getDMContext()); + if (future != null) future.cancel(false); + + // Check if there's a step pending, if so execute it + if (fStepQueues.containsKey(e.getDMContext())) { + List<StepRequest> queue = fStepQueues.get(e.getDMContext()); + final StepRequest request = queue.remove(queue.size() - 1); + if (queue.isEmpty()) fStepQueues.remove(e.getDMContext()); + fRunControl.canStep( + e.getDMContext(), request.fStepType, + new DataRequestMonitor<Boolean>(getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess() && getData()) { + fRunControl.step( + e.getDMContext(), request.fStepType, new RequestMonitor(getExecutor(), null)); + } else { + // For whatever reason we can't step anymore, so clear out + // the step queue. + fStepQueues.remove(e.getDMContext()); + } + } + }); + } + } + + @DsfServiceEventHandler + public void eventDispatched(final IResumedDMEvent e) { + if (e.getReason().equals(StateChangeReason.STEP)) { + fTimedOutFlags.put(e.getDMContext(), Boolean.FALSE); + // We shouldn't have a stepping timeout running unless we get two + // stepping events in a row without a suspended, which would be a + // protocol error. + assert !fTimedOutFutures.containsKey(e.getDMContext()); + fTimedOutFutures.put( + e.getDMContext(), + getExecutor().schedule( + new DsfRunnable() { public void run() { + fTimedOutFutures.remove(e.getDMContext()); + + if (getSession().isActive()) { + // Issue the stepping time-out event. + getSession().dispatchEvent( + new ISteppingTimedOutEvent() { + public IExecutionDMContext getDMContext() { return e.getDMContext(); } + }, + getProperties()); + } + }}, + STEPPING_TIMEOUT, TimeUnit.MILLISECONDS) + ); + + } + } + + @DsfServiceEventHandler + public void eventDispatched(ISteppingTimedOutEvent e) { + fTimedOutFlags.put(e.getDMContext(), Boolean.TRUE); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/CommandCache.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/CommandCache.java new file mode 100644 index 00000000000..0c0f1360da2 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/CommandCache.java @@ -0,0 +1,549 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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 + * Ericsson - Modified for caching commands corresponding to multiple execution contexts + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.service.command; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * This is a utility class for caching results of MI Commands. Return MIInfo + * data is retrieved from the cache if command was previously executed, and + * it is executed with MICommand service if it was not previously seen. + * + * Resetting the cache has to be performed by the object owning the cache when + * when an event indicates that the data is obsolete (which is specific to the + * types of commands being cached). + */ + +public class CommandCache implements ICommandListener +{ + static enum CommandStyle { COALESCED, NONCOALESCED } + + /** + * Holds cache information for a given command. + * @param <V> Type matches the result type associated with the command. + */ + class CommandInfo { + + /* + * Control variables. + */ + + /** List of the request monitors associated with this command */ + private final List<DataRequestMonitor<ICommandResult>> fCurrentRequestMonitors ; + + /** Original command. Need for reference from Queue completion notification */ + private final ICommand<ICommandResult> fCommand; + + /** Style of this command ( internal coalesced or not) */ + private final CommandStyle fCmdStyle; + + /** Command being processed for this command */ + private CommandInfo fCoalescedCmd; + + private ICommandToken fToken; + + public CommandInfo(CommandStyle cmdstyle, ICommand<ICommandResult> cmd, DataRequestMonitor<ICommandResult> rm ) { + fCmdStyle = cmdstyle; + fCommand = cmd; + fCurrentRequestMonitors = new LinkedList<DataRequestMonitor<ICommandResult>>(); + fCurrentRequestMonitors.add(rm); + fCoalescedCmd = null; + } + + public CommandStyle getCommandstyle() { return fCmdStyle; } + public List<DataRequestMonitor<ICommandResult>> getRequestMonitorList() { return fCurrentRequestMonitors; } + public ICommand<ICommandResult> getCommand() { return fCommand; } + public CommandInfo getCoalescedCmd() { return fCoalescedCmd; } + public void setCoalescedCmd( CommandInfo cmd ) { fCoalescedCmd = cmd; } + + @Override + public boolean equals(Object other) { + if (!(other instanceof CommandInfo)) return false; + CommandInfo otherCmd = (CommandInfo)other; + + return otherCmd.fCommand.equals(fCommand); + } + + @Override + public int hashCode() { + return fCommand.hashCode(); + } + } + + class CommandResultInfo { + private final ICommandResult fData; + private final IStatus fStatus; + + public CommandResultInfo(ICommandResult data, IStatus status) { + fData = data; + fStatus = status; + } + + public ICommandResult getData() { return fData; } + public IStatus getStatus() { return fStatus; } + } + + private DsfSession fSession; + + private ICommandControl fCommandControl; + + /* + * This class contains 5 significant lists. + * + * Cached Results : + * + * Contains a mapping of commands and their completed results. Until the cached + * results are cleared by the owner of the cache. + * + * Pending Commands Not Queued : + * + * The Control object has not yet indicated that it has recognized the command + * yet. The user is not allowed to interrogate these objects until the Control + * object indicates they have been queued ( commandQueued notification ). + * + * Pending Commands Unsent : + * + * This is the list of commands which have been issued to the Control object but + * have not been actually issued to the backend. These commands represent coalesce + * options. They may be compared against the Queued list being maintained by the + * Control object until told otherwise - commandSent notification ). + * + * Pending Commands Sent : + * + * This is a list of commands which have been issued to the Control object and + * have also been sent to the backend. It is not possible use these objects for + * coalescents. + * + * Coalesced Pending Q : + * + * These represent original commands for which a new coalesced command has been + * created. When the coalesced commands completes the results will be decomposed + * when back into individual results from this command. + */ + private Set<IDMContext> fAvailableContexts = new HashSet<IDMContext>(); + + private Map<IDMContext, HashMap<CommandInfo, CommandResultInfo>> fCachedContexts = new HashMap<IDMContext, HashMap<CommandInfo, CommandResultInfo>>(); + + private ArrayList<CommandInfo> fPendingQCommandsSent = new ArrayList<CommandInfo>(); + + private ArrayList<CommandInfo> fPendingQCommandsNotYetSent = new ArrayList<CommandInfo>(); + + private ArrayList<CommandInfo> fPendingQWaitingForCoalescedCompletion = new ArrayList<CommandInfo>(); + + public CommandCache(DsfSession session, ICommandControl control) { + fSession = session; + fCommandControl = control; + + /* + * We listen for the notifications that the commands have been sent to the + * backend from the GDB/MI Communications engine. + */ + fCommandControl.addCommandListener(this); + } + + /* + * Constructs a coalesced command if possible. + */ + private CommandInfo getCoalescedCommand(CommandInfo cmd) { + + for ( CommandInfo currentUnsentEntry : new ArrayList<CommandInfo>(fPendingQCommandsNotYetSent) ) { + /* + * Get the current unsent entry to determine if we can coalesced with it. + */ + ICommand<?> unsentCommand = currentUnsentEntry.getCommand(); + + /* + * Check if we can so construct a new COALESCED command from scratch. + */ + + // For sanity's sake, cast the generic ?'s to concrete types in the cache implementation. + @SuppressWarnings("unchecked") + ICommand<ICommandResult> coalescedCmd = + (ICommand<ICommandResult>)unsentCommand.coalesceWith( cmd.getCommand() ); + + if ( coalescedCmd != null ) { + CommandInfo coalescedCmdInfo = new CommandInfo( CommandStyle.COALESCED, coalescedCmd, null) ; + + if ( currentUnsentEntry.getCommandstyle() == CommandStyle.COALESCED ) { + /* + * We matched a command which is itself already a COALESCED command. So + * we need to run through the reference list and point all the current + * command which are referencing the command we just subsumed and change + * them to point to the new super command. + */ + + for ( CommandInfo waitingEntry : new ArrayList<CommandInfo>(fPendingQWaitingForCoalescedCompletion) ) { + + if ( waitingEntry.getCoalescedCmd() == currentUnsentEntry ) { + /* + * This referenced the old command change it to point to the new one. + */ + waitingEntry.setCoalescedCmd(coalescedCmdInfo); + } + } + } else { + /* + * This currently unsent entry needs to go into the coalescing list. To + * be completed when the coalesced command comes back with a result. + */ + fPendingQWaitingForCoalescedCompletion.add(currentUnsentEntry); + currentUnsentEntry.setCoalescedCmd(coalescedCmdInfo); + } + + /* + * Either way we want to take the command back from the Control object so it + * does not continue to process it. + */ + fPendingQCommandsNotYetSent.remove(currentUnsentEntry); + fCommandControl.removeCommand(currentUnsentEntry.fToken); + + return( coalescedCmdInfo ); + } + } + + return null; + } + + /** + * Executes given ICommand, or retrieves the cached result if known. + * @param command Command to execute. + * @param rm Return token, contains the retrieved MIInfo object as + * well as its cache status. + */ + public <V extends ICommandResult> void execute(ICommand<V> command, DataRequestMonitor<V> rm) { + assert fSession.getExecutor().isInExecutorThread(); + + // Cast the generic ?'s to concrete types in the cache implementation. + @SuppressWarnings("unchecked") + final ICommand<ICommandResult> genericCommand = (ICommand<ICommandResult>)command; + @SuppressWarnings("unchecked") + final DataRequestMonitor<ICommandResult> genericDone = (DataRequestMonitor<ICommandResult>) rm; + + CommandInfo cachedCmd = new CommandInfo( CommandStyle.NONCOALESCED, genericCommand, genericDone) ; + + final IDMContext context = genericCommand.getContext(); + + /* + * If command is already cached, just return the cached data. + */ + if(fCachedContexts.get(context) != null && fCachedContexts.get(context).containsKey(cachedCmd)){ + CommandResultInfo result = fCachedContexts.get(context).get(cachedCmd); + if (result.getStatus().getSeverity() <= IStatus.INFO) { + @SuppressWarnings("unchecked") + V v = (V)result.getData(); + rm.setData(v); + } else { + rm.setStatus(result.getStatus()); + } + rm.done(); + return; + } + + /* + * Return an error if the target is available anymore. + */ + if (!isTargetAvailable(command.getContext())) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Target not available.", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + /* + * If we are already waiting for this command to complete, + * add this request monitor to list of waiting monitors. + */ + for ( CommandInfo sentCommand : fPendingQCommandsSent ) { + if ( sentCommand.equals( cachedCmd )) { + sentCommand.getRequestMonitorList().add(genericDone); + return; + } + } + for ( CommandInfo notYetSentCommand : fPendingQCommandsNotYetSent ) { + if ( notYetSentCommand.equals( cachedCmd )) { + notYetSentCommand.getRequestMonitorList().add(genericDone); + return; + } + } + + + /* + * We see if this command can be combined into a coalesced one. The + * coalesce routine will take care of the already enqueued one which + * this command is being coalesced with. + */ + + CommandInfo coalescedCmd = getCoalescedCommand(cachedCmd); + + if ( coalescedCmd != null ) { + /* + * The original command we were handed needs to go into the waiting QUEUE. + * We also need to point it it to the coalesced command. + */ + fPendingQWaitingForCoalescedCompletion.add(cachedCmd); + cachedCmd.setCoalescedCmd(coalescedCmd); + cachedCmd = coalescedCmd; + } + + /* + * Now we have a command to send ( coalesced or not ). Put it in the cannot touch + * it list and give it to the Control object. Our state handlers will move it into + * the proper list as the Control object deals with it. + */ + final CommandInfo finalCachedCmd = cachedCmd; + fPendingQCommandsNotYetSent.add(finalCachedCmd); + + finalCachedCmd.fToken = fCommandControl.queueCommand( + finalCachedCmd.getCommand(), + new DataRequestMonitor<ICommandResult>(fSession.getExecutor(), null) { + @Override + public void handleCompleted() { + + /* + * Match this up with a command set we know about. + */ + if ( ! fPendingQCommandsSent.remove(finalCachedCmd) ) { + /* + * It should not be the case that this is possible. It would mean we + * have mismanaged the queues or completions are lost at the lower + * levels. When the removal and cancellation is completed this code + * will probably not be here. But for now just return. + */ + return ; + } + + ICommandResult result = getData(); + IStatus status = getStatus(); + + if ( finalCachedCmd.getCommandstyle() == CommandStyle.COALESCED ) { + /* + * We matched a command which is itself already a COALESCED command. So + * we need to go through the list of unsent commands which were not sent + * because the coalesced command represented it. For each match we find + * we create a new result from the coalesced command for it. + */ + + for ( CommandInfo waitingEntry : new ArrayList<CommandInfo>(fPendingQWaitingForCoalescedCompletion) ) { + + if ( waitingEntry.getCoalescedCmd() == finalCachedCmd ) { + + /* + * Remove this entry from the list since we can complete it. + */ + fPendingQWaitingForCoalescedCompletion.remove(waitingEntry); + + // Cast the calculated result back to the requested type. + @SuppressWarnings("unchecked") + V subResult = (V)result.getSubsetResult(waitingEntry.getCommand()); + CommandResultInfo subResultInfo = new CommandResultInfo(subResult, status); + + if(fCachedContexts.get(context) != null){ + fCachedContexts.get(context).put(waitingEntry, subResultInfo); + } else { + HashMap<CommandInfo, CommandResultInfo> map = new HashMap<CommandInfo, CommandResultInfo>(); + map.put(waitingEntry, subResultInfo); + fCachedContexts.put(context, map); + } + + if (!isSuccess()) { + + /* + * We had some form of error with the original command. So notify the + * original requesters of the issues. + */ + for (DataRequestMonitor<?> pendingRM : waitingEntry.getRequestMonitorList()) { + pendingRM.setStatus(status); + pendingRM.done(); + } + } else { + assert subResult != null; + + /* + * Notify the original requesters of the positive results. + */ + for (DataRequestMonitor<? extends ICommandResult> pendingRM : waitingEntry.getRequestMonitorList()) { + // Cast the pending return token to match the requested type. + @SuppressWarnings("unchecked") + DataRequestMonitor<V> vPendingRM = (DataRequestMonitor<V>) pendingRM; + + vPendingRM.setData(subResult); + vPendingRM.done(); + } + } + } + } + } else { + /* + * This is an original request which completed. Indicate success or + * failure to the original requesters. + */ + CommandResultInfo resultInfo = new CommandResultInfo(result, status); + + if (fCachedContexts.get(context) != null){ + fCachedContexts.get(context).put(finalCachedCmd, resultInfo); + } else { + HashMap<CommandInfo, CommandResultInfo> map = new HashMap<CommandInfo, CommandResultInfo>(); + map.put(finalCachedCmd, resultInfo); + fCachedContexts.put(context, map); + } + + if (!isSuccess()) { + /* + * We had some form of error with the original command. So notify the + * original requesters of the issues. + */ + for (DataRequestMonitor<?> pendingRM : finalCachedCmd.getRequestMonitorList()) { + pendingRM.setStatus(status); + pendingRM.done(); + } + } else { + // Cast the calculated result back to the requested type. + @SuppressWarnings("unchecked") + V vResult = (V)result; + + for (DataRequestMonitor<? extends ICommandResult> pendingRM : finalCachedCmd.getRequestMonitorList()) { + // Cast the pending return token to match the requested type. + @SuppressWarnings("unchecked") + DataRequestMonitor<V> vPendingRM = (DataRequestMonitor<V>) pendingRM; + + vPendingRM.setData(vResult); + vPendingRM.done(); + } + } + } + } + }); + } + + /** + * TODO + */ + public void setContextAvailable(IDMContext context, boolean isAvailable) { + if (isAvailable) { + fAvailableContexts.add(context); + } else { + fAvailableContexts.remove(context); + for (Iterator<IDMContext> itr = fAvailableContexts.iterator(); itr.hasNext();) { + if (DMContexts.isAncestorOf(context, itr.next())) { + itr.remove(); + } + } + } + } + + /** + * TODO + * @see #setContextAvailable(IDMContext, boolean) + */ + public boolean isTargetAvailable(IDMContext context) { + for (IDMContext availableContext : fAvailableContexts) { + if (context.equals(availableContext) || DMContexts.isAncestorOf(context, availableContext)) { + return true; + } + } + return false; + } + + + + /** + * Clears the cache data. + */ + public void reset() { + fCachedContexts.clear(); + } + + public void commandRemoved(ICommandToken token) { + /* + * Do nothing. + */ + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.mi.service.control.IDebuggerControl.ICommandListener#commandQueued(org.eclipse.cdt.dsf.mi.core.command.ICommand) + */ + public void commandQueued(ICommandToken token) { + /* + * Do nothing. + */ + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.mi.service.control.IDebuggerControl.ICommandListener#commandDone(org.eclipse.cdt.dsf.mi.core.command.ICommand, org.eclipse.cdt.dsf.mi.core.command.ICommandResult) + */ + public void commandDone(ICommandToken token, ICommandResult result) { + /* + * We handle the done with a runnable where we initiated the command + * so there is nothing to do here. + */ + } + + /* + * Move the command into our internal sent list. This means we can no longer look at + * this command for possible coalescence since it has been given to the debug engine + * and is currently being processed. + * + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.mi.service.control.IDebuggerControl.ICommandListener#commandSent(org.eclipse.cdt.dsf.mi.core.command.ICommand) + */ + public void commandSent(ICommandToken token) { + + // Cast the generic ?'s to concrete types in the cache implementation. + @SuppressWarnings("unchecked") + ICommand<ICommandResult> genericCommand = (ICommand<ICommandResult>)token.getCommand(); + + CommandInfo cachedCmd = new CommandInfo( CommandStyle.NONCOALESCED, genericCommand, null) ; + + for ( CommandInfo unqueuedCommand : new ArrayList<CommandInfo>(fPendingQCommandsNotYetSent) ) { + if ( unqueuedCommand.equals( cachedCmd )) { + fPendingQCommandsNotYetSent.remove(unqueuedCommand); + fPendingQCommandsSent.add(unqueuedCommand); + break; + } + } + } + + /** + * Clears the cache entries for given context. Clears the whole cache if + * context parameter is null. + */ + public void reset(IDMContext dmc) { + if (dmc == null) { + fCachedContexts.clear(); + return; + } + for (Iterator<IDMContext> itr = fCachedContexts.keySet().iterator(); itr.hasNext();) { + IDMContext keyDmc = itr.next(); + if (keyDmc != null && (dmc.equals(keyDmc) || DMContexts.isAncestorOf(keyDmc, dmc))) { + itr.remove(); + } + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommand.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommand.java new file mode 100644 index 00000000000..6c63ae8fb35 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommand.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems 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 + * Ericsson - Modified for additional features in DSF Reference implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.service.command; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; + + +/** + * Command interface for creating and manipulating GDB/MI commands + * for the DSF GDB reference implemenation. The command represents + * the GDB/MI request which will be put on the wire to the GDB + * backend. + */ + +public interface ICommand<V extends ICommandResult> { + /** + * Takes the supplied command and coalesces it with this one. + * The result is a new third command which represent the two + * original commands. + * <br>Note: the result type associated with the resurned command may be + * different than the result type associated with either of the commands + * being coalesced. + * + * @return newly created command, or null if command cannot be coalesced + */ + public ICommand<? extends ICommandResult> coalesceWith( ICommand<? extends ICommandResult> command ); + + /** + * Returns the context that this command is to be evaluated in. May be null + * if the command does not need to be evaluated in a specific context. + */ + public IDMContext getContext(); +} + + diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandControl.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandControl.java new file mode 100644 index 00000000000..dd174d3bd4a --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandControl.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.dsf.debug.service.command; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; + +/** + * API for sending commands to the debugger and for receiving command results + * and asynchronous events. The command control may be implemented by a service + * or a non-service object. + * + * @see ICommandControlService + */ +public interface ICommandControl { + + /** + * Adds the specified command to the queue of commands to be processed. + * + * @param command Specific command to be processed + * @param rm Request completion monitor + * @return None + */ + <V extends ICommandResult> ICommandToken queueCommand(ICommand<V> command, DataRequestMonitor<V> rm); + + /** + * Removes the specified command from the processor queue. + * + * @param command Specific command to be removed + * @return None + */ + void removeCommand(ICommandToken token); + + /** + * Adds a notification handler for the Command processor. + * + * @param command listener to be added + * @return None + */ + void addCommandListener(ICommandListener listener); + + /** + * Removes a notification handler for the Command processor. + * + * @param command listener to be removed + * @return None + */ + void removeCommandListener(ICommandListener listener); + + /** + * Adds a notification handler for the Event processor. + * + * @param event listener to be added + * @return None + */ + void addEventListener(IEventListener listener); + + /** + * Removes a notification handler for the Event processor. + * + * @param event listener to be removed + * @return None + */ + void removeEventListener(IEventListener listener); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandControlService.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandControlService.java new file mode 100644 index 00000000000..0672d5ec986 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandControlService.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems 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.cdt.dsf.debug.service.command; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.service.IDsfService; + +/** + * Service which acts as a command control. + * + * @since 1.1 + */ +public interface ICommandControlService extends ICommandControl, IDsfService { + + /** + * Context representing a command control service. All contexts which + * originate from a given command control service, should have that + * control's context in their hierarchy. + * + * @see ICommandControlService#getContext() + */ + public interface ICommandControlDMContext extends IDMContext { + /** + * Returns the ID of the command control that this context + * represents. + */ + public String getCommandControlId(); + } + + /** + * Event indicating that the back end process has started. + */ + public interface ICommandControlInitializedDMEvent extends IDMEvent<ICommandControlDMContext> {}; + + /** + * Event indicating that the back end process has terminated. + */ + public interface ICommandControlShutdownDMEvent extends IDMEvent<ICommandControlDMContext> {}; + + /** + * Returns the identifier of this command control service. It can be used + * to distinguish between multiple instances of command control services. + */ + public String getId(); + + /** + * returns the context representing this command control. + */ + public ICommandControlDMContext getContext(); + + /** + * Returns whether this command control is currently active. A command + * control service is active if it has been initialized and has not yet + * shut down. + * @return + */ + public boolean isActive(); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandListener.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandListener.java new file mode 100644 index 00000000000..f7a0f80ae79 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandListener.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.dsf.debug.service.command; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; + +/** + * Synchronous listener to commands being sent and received. + * All the registered listeners will be called in the same + * dispatch cycle as when the result of the command is submitted. + */ + +@ConfinedToDsfExecutor("") +public interface ICommandListener { + /** + * Notifies that the specified command has been added to the Command Queue. + * It has not yet been sent. In this state the command can be examined and + * possibly withdrawn because it has been coalesced with another command. + * + * @return None + * @param command Command which has been added to the Queue + */ + public void commandQueued(ICommandToken token); + + /** + * Notification that the given command was sent to the debugger. At this + * point the command is no longer in the Command Queue and should not be + * examined. The only thing which can be done is to try and cancel the + * command. + * + * @return None + * @param command + */ + public void commandSent(ICommandToken token); + + /** + * Notifies that the specified command has been removed from the + * Command Queue. This notification means that the command has + * been removed from the queue and not sent to the backend. The + * user has specifically removed it, perhaps because it has been + * combined with another. Or some state change has occured and + * there is no longer a need to get this particular set of data. + * + * @return None + * @param Command which has been sent to the backend + */ + public void commandRemoved(ICommandToken token); + + /** + * Notifies that the specified command has been completed. + * + * @return None + * @param Command which has been sent to the backend + */ + public void commandDone(ICommandToken token, ICommandResult result); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandResult.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandResult.java new file mode 100644 index 00000000000..09e53154c43 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandResult.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems 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.cdt.dsf.debug.service.command; + +public interface ICommandResult { + /** + * Returns an ICommandResult which is a subset command result. The command + * result which is being passed in is from a coalesced command. The result + * which is desired is contained within those results. In this instance we + * are processing the command result from the coalesced command to get our + * command result. + * <i>Note:</i> The type of returned command result must match the type + * associated with the subset command that is passed in the argument. + * + * @return result for this particular command. + */ + public <V extends ICommandResult> V getSubsetResult( ICommand<V> command ); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandToken.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandToken.java new file mode 100644 index 00000000000..ccd80d234d2 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandToken.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems 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.cdt.dsf.debug.service.command; + +/** + * Token returned by ICommandControl.queueCommand(). This token can be used + * to uniquely identify a command when calling ICommandControl.removeCommand() + * or when implementing the ICommandListener listener methods. + */ +public interface ICommandToken { + /** + * Returns the command that this was created for. + */ + public ICommand<? extends ICommandResult> getCommand(); +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/IEventListener.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/IEventListener.java new file mode 100644 index 00000000000..49fa20f3eee --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/IEventListener.java @@ -0,0 +1,19 @@ +package org.eclipse.cdt.dsf.debug.service.command; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; + +/** + * Synchronous listener for events issued from the debugger. All + * registered listeners will be called in the same dispatch cycle. + */ + +@ConfinedToDsfExecutor("") +public interface IEventListener { + /** + * Notifies that the given asynchronous output was received from the + * debugger. + * @param output output that was received from the debugger. Format + * of the output data is debugger specific. + */ + public void eventReceived(Object output); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/sourcelookup/DsfSourceLookupDirector.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/sourcelookup/DsfSourceLookupDirector.java new file mode 100644 index 00000000000..2e881b18c6c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/sourcelookup/DsfSourceLookupDirector.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2004, 2008 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Nokia - Added support for AbsoluteSourceContainer( 159833 ) + * Wind River Systems - Adapted for use with DSF +*******************************************************************************/ +package org.eclipse.cdt.dsf.debug.sourcelookup; + +import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant; + +/** + * DSF source lookup director. + */ +public class DsfSourceLookupDirector extends CSourceLookupDirector { + + private final DsfSession fSession; + + public DsfSourceLookupDirector(DsfSession session) { + fSession = session; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.sourcelookup.ISourceLookupDirector#initializeParticipants() + */ + @Override + public void initializeParticipants() { + super.initializeParticipants(); + addParticipants( new ISourceLookupParticipant[]{ new DsfSourceLookupParticipant(fSession) } ); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/sourcelookup/DsfSourceLookupParticipant.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/sourcelookup/DsfSourceLookupParticipant.java new file mode 100644 index 00000000000..11a105e6f2e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/sourcelookup/DsfSourceLookupParticipant.java @@ -0,0 +1,229 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems 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.cdt.dsf.debug.sourcelookup; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IStack; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMData; +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.sourcelookup.ISourceContainer; +import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector; +import org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant; + +/** + * Source lookup participant that should be used with DSF-based debuggers. + */ +@ThreadSafe +public class DsfSourceLookupParticipant implements ISourceLookupParticipant { + protected static final Object[] EMPTY = new Object[0]; + + private DsfExecutor fExecutor; + private String fSessionId; + private DsfServicesTracker fServicesTracker; + private ISourceLookupDirector fDirector; + private Map<String, List<Object>> fLookupCache = Collections.synchronizedMap(new HashMap<String, List<Object>>()); + + public DsfSourceLookupParticipant(DsfSession session) { + fSessionId = session.getId(); + fExecutor = session.getExecutor(); + fServicesTracker = new DsfServicesTracker(DsfPlugin.getBundleContext(), fSessionId); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant#init(org.eclipse.debug.core.sourcelookup.ISourceLookupDirector) + */ + public void init(ISourceLookupDirector director) { + fDirector = director; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant#dispose() + */ + public void dispose() { + fServicesTracker.dispose(); + fDirector = null; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant#findSourceElements(java.lang.Object) + */ + public Object[] findSourceElements(Object object) throws CoreException { + CoreException single = null; + MultiStatus multiStatus = null; + List<Object> results = null; + + String name = getSourceName(object); + if (name != null) { + results = fLookupCache.get(name); + if (results != null) { + return results.toArray(); + } else { + results = new ArrayList<Object>(); + } + ISourceContainer[] containers = getSourceContainers(); + for (int i = 0; i < containers.length; i++) { + try { + ISourceContainer container = containers[i]; + if (container != null) { + Object[] objects = container.findSourceElements(name); + if (objects.length > 0) { + if (isFindDuplicates()) { + results.addAll(Arrays.asList(objects)); + } else { + results.add(objects[0]); + break; + } + } + } + } catch (CoreException e) { + if (single == null) { + single = e; + } else if (multiStatus == null) { + multiStatus = new MultiStatus(DebugPlugin.getUniqueIdentifier(), DebugPlugin.INTERNAL_ERROR, new IStatus[]{single.getStatus()}, "Source Lookup error", null); //$NON-NLS-1$ + multiStatus.add(e.getStatus()); + } else { + multiStatus.add(e.getStatus()); + } + } + } + + if (!results.isEmpty()) { + synchronized(fLookupCache) { + if (!fLookupCache.containsKey(name)) { + fLookupCache.put(name, results); + } + } + } + } + if (results == null || results.isEmpty()) { + if (multiStatus != null) { + throw new CoreException(multiStatus); + } else if (single != null) { + throw single; + } + return EMPTY; + } + return results.toArray(); + } + + /** + * Returns whether this participant's source lookup director is configured + * to search for duplicate source elements. + * + * @return whether this participant's source lookup director is configured + * to search for duplicate source elements + */ + protected boolean isFindDuplicates() { + ISourceLookupDirector director = fDirector; + if (director != null) { + return director.isFindDuplicates(); + } + return false; + } + + /** + * Returns the source containers currently registered with this participant's + * source lookup director. + * + * @return the source containers currently registered with this participant's + * source lookup director + */ + protected ISourceContainer[] getSourceContainers() { + ISourceLookupDirector director = fDirector; + if (director != null) { + return director.getSourceContainers(); + } + return new ISourceContainer[0]; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant#sourceContainersChanged(org.eclipse.debug.core.sourcelookup.ISourceLookupDirector) + */ + public void sourceContainersChanged(ISourceLookupDirector director) { + fLookupCache.clear(); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.core.sourcelookup.ISourceLookupParticipant#getSourceName(java.lang.Object) + */ + public String getSourceName(Object object) throws CoreException { + if ( !(object instanceof IDMContext) || + !((IDMContext)object).getSessionId().equals(fSessionId) ) + { + throw new CoreException(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, -1, "Invalid object", null)); //$NON-NLS-1$ + } + + final IDMContext dmc = (IDMContext)object; + Query<String> query = new Query<String>() { + @Override + protected void execute(final DataRequestMonitor<String> rm) { + getSourceNameOnDispatchThread(dmc, rm); + }}; + fExecutor.execute(query); + try { + return query.get(); + } catch (InterruptedException e) { assert false : "Interrupted exception in DSF executor"; //$NON-NLS-1$ + } catch (ExecutionException e) { + if (e.getCause() instanceof CoreException) { + throw (CoreException)e.getCause(); + } + assert false : "Unexptected exception"; //$NON-NLS-1$ + } + return null; // Should never get here. + } + + @ConfinedToDsfExecutor("fExecutor") + private void getSourceNameOnDispatchThread(IDMContext dmc, final DataRequestMonitor<String> rm) { + if (!(dmc instanceof IStack.IFrameDMContext)) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, "No source for this object", null)); //$NON-NLS-1$ + rm.done(); + return; + } + IFrameDMContext frameDmc = (IFrameDMContext)dmc; + + IStack stackService = fServicesTracker.getService(IStack.class); + if (stackService == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, "Stack data not available", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + stackService.getFrameData( + frameDmc, + new DataRequestMonitor<IFrameDMData>(fExecutor, rm) { @Override + public void handleSuccess() { + rm.setData(getData().getFile()); + rm.done(); + }}); + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/internal/DsfPlugin.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/internal/DsfPlugin.java new file mode 100644 index 00000000000..11ccf06857b --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/internal/DsfPlugin.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.dsf.internal; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Plugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class DsfPlugin extends Plugin { + + // The plug-in ID + public static final String PLUGIN_ID = "org.eclipse.cdt.dsf"; //$NON-NLS-1$ + + // The shared instance + private static DsfPlugin fgPlugin; + + // BundleContext of this plugin + private static BundleContext fgBundleContext; + + // Debugging flag + public static boolean DEBUG = false; + + /** + * The constructor + */ + public DsfPlugin() { + fgPlugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.core.runtime.Plugins#start(org.osgi.framework.BundleContext) + */ + @Override + public void start(BundleContext context) throws Exception { + fgBundleContext = context; + super.start(context); + DEBUG = "true".equals(Platform.getDebugOption("org.eclipse.cdt.dsf/debug")); //$NON-NLS-1$//$NON-NLS-2$ + } + + /* + * (non-Javadoc) + * @see org.eclipse.core.runtime.Plugin#stop(org.osgi.framework.BundleContext) + */ + @Override + public void stop(BundleContext context) throws Exception { + fgBundleContext = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static DsfPlugin getDefault() { + return fgPlugin; + } + + public static BundleContext getBundleContext() { + return fgBundleContext; + } + + public static void debug(String message) { + if (DEBUG) { + System.out.println(message); + } + } + + public static String getDebugTime() { + StringBuilder traceBuilder = new StringBuilder(); + + // Record the time + long time = System.currentTimeMillis(); + long seconds = (time / 1000) % 1000; + if (seconds < 100) traceBuilder.append('0'); + if (seconds < 10) traceBuilder.append('0'); + traceBuilder.append(seconds); + traceBuilder.append(','); + long millis = time % 1000; + if (millis < 100) traceBuilder.append('0'); + if (millis < 10) traceBuilder.append('0'); + traceBuilder.append(millis); + return traceBuilder.toString(); + } + + + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/AbstractDsfService.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/AbstractDsfService.java new file mode 100644 index 00000000000..8d085eb6c27 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/AbstractDsfService.java @@ -0,0 +1,222 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.dsf.service; + +import java.util.Arrays; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceRegistration; + + +/** + * Standard base implementation of the DSF service. This is a convenience + * class that provides the basic functionality that all DSF services have + * to implement. + */ +abstract public class AbstractDsfService + implements IDsfService, IDsfStatusConstants +{ + /** Reference to the session that this service belongs to. */ + private DsfSession fSession; + + /** Startup order number of this service. */ + private int fStartupNumber; + + /** Registration object for this service. */ + private ServiceRegistration fRegistration; + + /** Tracker for services that this service depends on. */ + private DsfServicesTracker fTracker; + + /** Properties that this service was registered with */ + @SuppressWarnings("unchecked") + private Dictionary fProperties; + + /** Properties that this service was registered with */ + private String fFilter; + + /** + * Only constructor, requires a reference to the session that this + * service belongs to. + * @param session + */ + public AbstractDsfService(DsfSession session) { + fSession = session; + } + + public DsfExecutor getExecutor() { return fSession.getExecutor(); } + + @SuppressWarnings("unchecked") + public Dictionary getProperties() { return fProperties; } + + public String getServiceFilter() { return fFilter; } + + public int getStartupNumber() { return fStartupNumber; } + + public void initialize(RequestMonitor rm) { + fTracker = new DsfServicesTracker(getBundleContext(), fSession.getId()); + fStartupNumber = fSession.getAndIncrementServiceStartupCounter(); + rm.done(); + } + + public void shutdown(RequestMonitor rm) { + fTracker.dispose(); + fTracker = null; + rm.done(); + } + + /** Returns the session object for this service */ + public DsfSession getSession() { return fSession; } + + /** + * Sub-classes should return the bundle context of the plugin, which the + * service belongs to. + */ + abstract protected BundleContext getBundleContext(); + + /** Returns the tracker for the services that this service depends on. */ + protected DsfServicesTracker getServicesTracker() { return fTracker; } + + /** + * Registers this service. + */ + @SuppressWarnings("unchecked") + protected void register(String[] classes, Dictionary properties) { + + /* + * If this service has already been registered, make sure we + * keep the names it has been registered with. However, we + * must trigger a new registration or else OSGI will keep the two + * registration separate. + */ + if (fRegistration != null) { + String[] previousClasses = (String[])fRegistration.getReference().getProperty(Constants.OBJECTCLASS); + + // Use a HashSet to avoid duplicates + Set<String> newClasses = new HashSet<String>(); + newClasses.addAll(Arrays.asList(previousClasses)); + newClasses.addAll(Arrays.asList(classes)); + classes = newClasses.toArray(new String[0]); + + /* + * Also keep all previous properties. + */ + if (fProperties != null) { + for (Enumeration e = fProperties.keys() ; e.hasMoreElements();) { + Object key = e.nextElement(); + Object value = fProperties.get(key); + properties.put(key, value); + } + } + + // Now, cancel the previous registration + unregister(); + } + /* + * Ensure that the list of classes contains the base DSF service + * interface, as well as the actual class type of this object. + */ + if (!Arrays.asList(classes).contains(IDsfService.class.getName())) { + String[] newClasses = new String[classes.length + 1]; + System.arraycopy(classes, 0, newClasses, 1, classes.length); + newClasses[0] = IDsfService.class.getName(); + classes = newClasses; + } + if (!Arrays.asList(classes).contains(getClass().getName())) { + String[] newClasses = new String[classes.length + 1]; + System.arraycopy(classes, 0, newClasses, 1, classes.length); + newClasses[0] = getClass().getName(); + classes = newClasses; + } + /* + * Make sure that the session ID is set in the service properties. + * The session ID distinguishes this service instance from instances + * of the same service in other sessions. + */ + properties.put(PROP_SESSION_ID, getSession().getId()); + fProperties = properties; + fRegistration = getBundleContext().registerService(classes, this, properties); + + /* + * Retrieve the OBJECTCLASS property directly from the service + * registration info. This is the best bet for getting an accurate + * value. + */ + fRegistration.getReference().getProperty(Constants.OBJECTCLASS); + fProperties.put(Constants.OBJECTCLASS, fRegistration.getReference().getProperty(Constants.OBJECTCLASS)); + + /* + * Create the filter for this service based on all the properties. If + * there is a single service instance per session, or if the properties + * parameter uniquely identifies this service instance among other + * instances in this session. Then this filter will fetch this service + * and only this service from OSGi. + */ + fFilter = generateFilter(fProperties); + } + + /** + * Generates an LDAP filter to uniquely identify this service. + */ + @SuppressWarnings("unchecked") + private String generateFilter(Dictionary properties) { + StringBuffer filter = new StringBuffer(); + filter.append("(&"); //$NON-NLS-1$ + + for (Enumeration keys = properties.keys(); keys.hasMoreElements();) { + Object key = keys.nextElement(); + Object value = properties.get(key); + if (value instanceof Object[]) { + /* + * For arrays, add a test to check that every element in array + * is present. This is here mainly to handle OBJECTCLASS property. + */ + for (Object arrayValue : (Object[])value) { + filter.append('('); + filter.append(key.toString()); + filter.append("=*"); //$NON-NLS-1$ + filter.append(arrayValue.toString()); + filter.append(')'); + } + } else { + filter.append('('); + filter.append(key.toString()); + filter.append('='); + filter.append(value.toString()); + filter.append(')'); + } + } + filter.append(')'); + return filter.toString(); + } + + /** + * De-registers this service. + * + */ + protected void unregister() { + if (fRegistration != null) { + fRegistration.unregister(); + } + fRegistration = null; + } + + /** Returns the registration object that was obtained when this service was registered */ + protected ServiceRegistration getServiceRegistration() { return fRegistration; } +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfServiceEventHandler.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfServiceEventHandler.java new file mode 100644 index 00000000000..9f2e5525dcb --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfServiceEventHandler.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems 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.cdt.dsf.service; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation for service event handler methods. The name of the event + * handler method is irrelevant, only the annotation is checked. + * <p> + * Each service event handler method should have one or two parameters: + * <li> + * <br> First argument is required and it should be the event object, with + * type with the event class desired. + * <br> Second argument is optional, and it has to be of type Dictionary<String,String>. + * If this parameter is declared, the handler will be passed the properties + * dictionary of the service that submitted the event. + * </li> + * <p> + * It is expected that service event classes are hierarchical. So that if a + * handler is registered for a super-class of another event, this handler + * will be called every time one of the sub-class events is invoked. + * If a listener declares a handler for an event AND a superclass of that event, + * both handlers will be invoked when the event is dispatched. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +public @interface DsfServiceEventHandler { + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfServices.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfServices.java new file mode 100644 index 00000000000..02b83b94b1d --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfServices.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems 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.cdt.dsf.service; + +/** + * Utility class containing status methods to use with DSF services. + */ +public class DsfServices { + + /** + * Creates a properly formatted OSGi service filter for a DSF service based + * on service class and session ID. + * @param serviceClass Class of the service to create the filter for. + * @param sessionId Session ID of the session that the service belongs to. + * @return Filter string to identify the given service. + */ + public static String createServiceFilter(Class<?> serviceClass, String sessionId) { + String serviceId = + "(&" + //$NON-NLS-1$ + "(OBJECTCLASS=" + //$NON-NLS-1$ + serviceClass.getName() + + ")" + //$NON-NLS-1$ + "(" + //$NON-NLS-1$ + IDsfService.PROP_SESSION_ID + + "=" + //$NON-NLS-1$ + sessionId + + ")" + //$NON-NLS-1$ + ")" ; //$NON-NLS-1$ + + return serviceId; + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfServicesTracker.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfServicesTracker.java new file mode 100644 index 00000000000..9a98805798c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfServicesTracker.java @@ -0,0 +1,169 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.dsf.service; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; + +/** + * Convenience class to help track DSF services that a given + * client needs to use. This class is similar to the standard OSGI + * org.osgi.util.tracker.ServiceTracker class, with a few differences: + * <br>1. This class is assumed to be accessed by a single thread hence it + * has no synchronization built in, while OSGI ServiceTracker synchronized + * access to its data. + * <br>2. This class is primarily designed to track multiple services of + * different type (class), while OSGI ServiceTracker is designed to work with + * single class type, with optional filtering options. + * <br>3. This class uses knowledge of DSF sessions to help narrow down + * service references. + * <br>4. OSGI Service tracker explicitly listens to OSGI service + * startup/shutdown events and it will clear a reference to a service as + * soon as it's shut down. This class leaves it up to the client to make + * sure that it doesn't access a service once that service has been shut down. + * <p> + * That said, it might be more convenient for certain types of clients to use + * OSGI Service tracker for the additional features it provides. + * + * @see org.osgi.util.tracker.ServiceTracker + */ +public class DsfServicesTracker { + + private static String getServiceFilter(String sessionId) { + return ("(" + IDsfService.PROP_SESSION_ID + "=" + sessionId + ")").intern(); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ + } + + private static class ServiceKey + { + String fClassName; + String fFilter; + public ServiceKey(Class<?> clazz, String filter) { + fClassName = clazz != null ? clazz.getName() : null; + fFilter = filter; + } + + @Override + public boolean equals(Object other) { + // I guess this doesn't have to assume fFilter can be null, but oh well. + return other instanceof ServiceKey && + ((fClassName == null && ((ServiceKey)other).fClassName == null) || + (fClassName != null && fClassName.equals(((ServiceKey)other).fClassName))) && + ((fFilter == null && ((ServiceKey)other).fFilter == null) || + (fFilter != null && fFilter.equals(((ServiceKey)other).fFilter))); + } + + @Override + public int hashCode() { + return (fClassName == null ? 0 : fClassName.hashCode()) + (fFilter == null ? 0 : fFilter.hashCode()); + } + } + + private BundleContext fBundleContext; + private Map<ServiceKey,ServiceReference> fServiceReferences = new HashMap<ServiceKey,ServiceReference>(); + private Map<ServiceReference,Object> fServices = new HashMap<ServiceReference,Object>(); + private String fServiceFilter; + + /** + * Only constructor. + * @param bundleContext Context of the plugin that the client lives in. + * @param sessionId The DSF session that this tracker will be used for. + */ + public DsfServicesTracker(BundleContext bundleContext, String sessionId) { + fBundleContext = bundleContext; + fServiceFilter = getServiceFilter(sessionId); + } + + /** + * Retrieves a service reference for given service class and optional filter. + * Filter should be used if there are multiple instances of the desired service + * running within the same session. + * @param serviceClass class of the desired service + * @param custom filter to use when searching for the service, this filter will + * be used instead of the standard filter so it should also specify the desired + * session-ID + * @return OSGI service reference object to the desired service, null if not found + */ + @SuppressWarnings("unchecked") + public ServiceReference getServiceReference(Class serviceClass, String filter) { + ServiceKey key = new ServiceKey(serviceClass, filter != null ? filter : fServiceFilter); + if (fServiceReferences.containsKey(key)) { + return fServiceReferences.get(key); + } + + try { + ServiceReference[] references = fBundleContext.getServiceReferences(key.fClassName, key.fFilter); + assert references == null || references.length <= 1; + if (references == null || references.length == 0) { + return null; + } else { + fServiceReferences.put(key, references[0]); + return references[0]; + } + } catch(InvalidSyntaxException e) { + assert false : "Invalid session ID syntax"; //$NON-NLS-1$ + } catch(IllegalStateException e) { + // Can occur when plugin is shutting down. + } + return null; + } + + /** + * Convenience class to retrieve a service based on class name only. + * @param serviceClass class of the desired service + * @return instance of the desired service, null if not found + */ + public <V> V getService(Class<V> serviceClass) { + return getService(serviceClass, null); + } + + /** + * Retrieves the service given service class and optional filter. + * Filter should be used if there are multiple instances of the desired service + * running within the same session. + * @param serviceClass class of the desired service + * @param custom filter to use when searching for the service, this filter will + * be used instead of the standard filter so it should also specify the desired + * session-ID + * @return instance of the desired service, null if not found + */ + @SuppressWarnings("unchecked") + public <V> V getService(Class<V> serviceClass, String filter) { + ServiceReference serviceRef = getServiceReference(serviceClass, filter); + if (serviceRef == null) { + return null; + } else { + if (fServices.containsKey(serviceRef)) { + return (V)fServices.get(serviceRef); + } else { + V service = (V)fBundleContext.getService(serviceRef); + fServices.put(serviceRef, service); + return service; + } + } + } + + /** + * Un-gets all the serferences held by this tracker. Must be called + * to avoid leaking OSGI service references. + */ + public void dispose() { + for (Iterator<ServiceReference> itr = fServices.keySet().iterator(); itr.hasNext();) { + fBundleContext.ungetService(itr.next()); + itr.remove(); + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfSession.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfSession.java new file mode 100644 index 00000000000..349ae16447e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfSession.java @@ -0,0 +1,431 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.dsf.service; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.Filter; + +/** + * Class to manage DSF sessions. A DSF session is a way to + * associate a set of DSF services that are running simultaneously and + * are interacting with each other to provide a complete set of functionality. + * <p> + * Properties of a session are following: + * <br>1. Each session is associated with a single DSF executor, although there + * could be multiple sessions using the same executor. + * <br>2. Each session has a unique String identifier, which has to be used by + * the services belonging to this session when registering with OSGI services. + * <br>3. Each session has its set of service event listeners. + * <br>4. Start and end of each session is announced by events, which are always + * sent on that session's executor dispatch thread. + * + * @see org.eclipse.cdt.dsf.concurrent.DsfExecutor + */ +@ConfinedToDsfExecutor("getExecutor") +public class DsfSession +{ + /** + * Listener for session started events. This listener is always going to be + * called in the dispatch thread of the session's executor. + */ + public static interface SessionStartedListener { + /** + * Called when a new session is started. It is always called in the + * dispatch thread of the new session. + */ + public void sessionStarted(DsfSession session); + } + + /** + * Listener for session ended events. This listener is always going to be + * called in the dispatch thread of the session's executor. + */ + public static interface SessionEndedListener { + /** + * Called when a session is ended. It is always called in the + * dispatch thread of the session. + */ + public void sessionEnded(DsfSession session); + } + + private static int fgSessionIdCounter = 0; + private static Set<DsfSession> fgActiveSessions = Collections.synchronizedSet(new HashSet<DsfSession>()); + private static List<SessionStartedListener> fSessionStartedListeners = Collections.synchronizedList(new ArrayList<SessionStartedListener>()); + private static List<SessionEndedListener> fSessionEndedListeners = Collections.synchronizedList(new ArrayList<SessionEndedListener>()); + + /** Returns true if given session is currently active */ + public static boolean isSessionActive(String sessionId) { + return getSession(sessionId) != null; + } + + /** Returns a session instance for given session identifier */ + @ThreadSafe + public static DsfSession getSession(String sessionId) { + synchronized(fgActiveSessions) { + for (DsfSession session : fgActiveSessions) { + if (session.getId().equals(sessionId)) { + return session; + } + } + } + return null; + } + + /** + * Registers a listener for session started events. + * Can be called on any thread. + */ + @ThreadSafe + public static void addSessionStartedListener(SessionStartedListener listener) { + assert !fSessionStartedListeners.contains(listener); + fSessionStartedListeners.add(listener); + } + + /** + * Un-registers a listener for session started events. + * Can be called on any thread. + */ + @ThreadSafe + public static void removeSessionStartedListener(SessionStartedListener listener) { + assert fSessionStartedListeners.contains(listener); + fSessionStartedListeners.remove(listener); + } + + /** + * Registers a listener for session ended events. + * Can be called on any thread. + */ + @ThreadSafe + public static void addSessionEndedListener(SessionEndedListener listener) { + assert !fSessionEndedListeners.contains(listener); + fSessionEndedListeners.add(listener); + } + + /** + * Un-registers a listener for session ended events. + * Can be called on any thread. + */ + @ThreadSafe + public static void removeSessionEndedListener(SessionEndedListener listener) { + assert fSessionEndedListeners.contains(listener); + fSessionEndedListeners.remove(listener); + } + + /** + * Starts and returns a new session instance. This method can be called on any + * thread, but the session-started listeners will be called using the session's + * executor. + * @param executor The DSF executor to use for this session. + * @param ownerId ID (plugin ID preferably) of the owner of this session + * @return instance object of the new session + */ + @ThreadSafe + public static DsfSession startSession(DsfExecutor executor, String ownerId) { + synchronized(fgActiveSessions) { + final DsfSession newSession = new DsfSession(executor, ownerId, Integer.toString(fgSessionIdCounter++)); + fgActiveSessions.add(newSession); + executor.submit( new DsfRunnable() { public void run() { + SessionStartedListener[] listeners = fSessionStartedListeners.toArray( + new SessionStartedListener[fSessionStartedListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].sessionStarted(newSession); + } + }}); + return newSession; + } + } + + /** + * Terminates the given session. This method can be also called on any + * thread, but the session-ended listeners will be called using the session's + * executor. + * @param session session to terminate + */ + @ThreadSafe + public static void endSession(final DsfSession session) { + synchronized(fgActiveSessions) { + if (!fgActiveSessions.contains(session)) { + throw new IllegalArgumentException(); + } + fgActiveSessions.remove(session); + session.getExecutor().submit( new DsfRunnable() { public void run() { + SessionEndedListener[] listeners = fSessionEndedListeners.toArray( + new SessionEndedListener[fSessionEndedListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].sessionEnded(session); + } + }}); + } + } + + private static class ListenerEntry { + Object fListener; + Filter fFilter; + + ListenerEntry(Object listener, Filter filter) { + fListener = listener; + fFilter = filter; + } + + @Override + public boolean equals(Object other) { + return other instanceof ListenerEntry && fListener.equals(((ListenerEntry)other).fListener); + } + + @Override + public int hashCode() { return fListener.hashCode(); } + } + + /** ID (plugin ID preferably) of the owner of this session */ + private String fOwnerId; + + /** Session ID of this session. */ + private String fId; + + /** Dispatch-thread executor for this session */ + private DsfExecutor fExecutor; + + /** Service start-up counter for this session */ + private int fServiceInstanceCounter; + + /** Map of registered event listeners. */ + private Map<ListenerEntry,Method[]> fListeners = new HashMap<ListenerEntry,Method[]>(); + + /** + * Map of registered adapters, for implementing the + * IModelContext.getAdapter() method. + * @see org.eclipse.cdt.dsf.datamodel.AbstractDMContext#getAdapter + */ + @SuppressWarnings("unchecked") + private Map<Class,Object> fAdapters = Collections.synchronizedMap(new HashMap<Class,Object>()); + + /** Returns the owner ID of this session */ + public String getOwnerId() { return fOwnerId; } + + public boolean isActive() { return DsfSession.isSessionActive(fId); } + + /** Returns the ID of this session */ + public String getId() { return fId; } + + /** Returns the DSF executor of this session */ + public DsfExecutor getExecutor() { return fExecutor; } + + /** + * Adds a new listener for service events in this session. + * @param listener the listener that will receive service events + * @param filter optional filter to restrict the services that the + * listener will receive events from + */ + public void addServiceEventListener(Object listener, Filter filter) { + ListenerEntry entry = new ListenerEntry(listener, filter); + assert !fListeners.containsKey(entry); + fListeners.put(entry, getEventHandlerMethods(listener)); + } + + /** + * Removes the given listener. + * @param listener listener to remove + */ + public void removeServiceEventListener(Object listener) { + ListenerEntry entry = new ListenerEntry(listener, null); + assert fListeners.containsKey(entry); + fListeners.remove(entry); + } + + /** + * Retrieves and increments the startup counter for services in this session. + * DSF services should retrieve this counter when they are initialized, + * and should return it through IService.getStartupNumber(). This number is then + * used to prioritize service events. + * @return current startup counter value + */ + public int getAndIncrementServiceStartupCounter() { return fServiceInstanceCounter++; } + + /** + * Dispatches the given event to service event listeners. The event is submitted to + * the executor to be dispatched. + * @param event to be sent out + * @param serviceProperties properties of the service requesting the event to be dispatched + */ + @ThreadSafe + @SuppressWarnings("unchecked") + public void dispatchEvent(final Object event, final Dictionary serviceProperties) { + getExecutor().submit(new DsfRunnable() { + public void run() { doDispatchEvent(event, serviceProperties);} + @Override + public String toString() { return "Event: " + event + ", from service " + serviceProperties; } //$NON-NLS-1$ //$NON-NLS-2$ + }); + } + + /** + * Registers a IModelContext adapter of given type. + * @param adapterType class type to register the adapter for + * @param adapter adapter instance to register + * @see org.eclipse.dsdp.model.AbstractDMContext#getAdapter + */ + @ThreadSafe + @SuppressWarnings("unchecked") + public void registerModelAdapter(Class adapterType, Object adapter) { + fAdapters.put(adapterType, adapter); + } + + /** + * Un-registers a IModelContext adapter of given type. + * @param adapterType adapter type to unregister + * @see org.eclipse.dsdp.model.AbstractDMContext#getAdapter + */ + @ThreadSafe + @SuppressWarnings("unchecked") + public void unregisterModelAdapter(Class adapterType) { + fAdapters.remove(adapterType); + } + + /** + * Retrieves an adapter for given type for IModelContext. + * @param adapterType adapter type to look fors + * @return adapter object for given type, null if none is registered with the session + * @see org.eclipse.dsdp.model.AbstractDMContext#getAdapter + */ + @ThreadSafe + @SuppressWarnings("unchecked") + public Object getModelAdapter(Class adapterType) { + return fAdapters.get(adapterType); + } + + @Override + @ThreadSafe + public boolean equals(Object other) { + return other instanceof DsfSession && fId.equals(((DsfSession)other).fId); + } + + @Override + @ThreadSafe + public int hashCode() { return fId.hashCode(); } + + @SuppressWarnings("unchecked") + private void doDispatchEvent(Object event, Dictionary serviceProperties) { + // Build a list of listeners; + SortedMap<ListenerEntry,List<Method>> listeners = new TreeMap<ListenerEntry,List<Method>>(new Comparator<ListenerEntry>() { + public int compare(ListenerEntry o1, ListenerEntry o2) { + if (o1.fListener == o2.fListener) { + return 0; + } if (o1.fListener instanceof IDsfService && !(o2.fListener instanceof IDsfService)) { + return Integer.MIN_VALUE; + } else if (o2.fListener instanceof IDsfService && !(o1.fListener instanceof IDsfService)) { + return Integer.MAX_VALUE; + } else if ( (o1.fListener instanceof IDsfService) && (o2.fListener instanceof IDsfService) ) { + return ((IDsfService)o1.fListener).getStartupNumber() - ((IDsfService)o2.fListener).getStartupNumber(); + } + return 1; + } + + @Override + public boolean equals(Object obj) { + return obj == this; + } + }); + + // Build a list of listeners and methods that are registered for this event class. + Class<?> eventClass = event.getClass(); + for (Map.Entry<ListenerEntry,Method[]> entry : fListeners.entrySet()) { + if (entry.getKey().fFilter != null && !entry.getKey().fFilter.match(serviceProperties)) { + // Dispatching service doesn't match the listener's filter, skip it. + continue; + } + Method[] allMethods = entry.getValue(); + List<Method> matchingMethods = new ArrayList<Method>(); + for (Method method : allMethods) { + assert method.getParameterTypes().length > 0 : eventClass.getName() + "." + method.getName() //$NON-NLS-1$ + + " signature contains zero parameters"; //$NON-NLS-1$ + if ( method.getParameterTypes()[0].isAssignableFrom(eventClass) ) { + matchingMethods.add(method); + } + } + if (!matchingMethods.isEmpty()) { + listeners.put(entry.getKey(), matchingMethods); + } + } + + // Call the listeners + for (Map.Entry<ListenerEntry,List<Method>> entry : listeners.entrySet()) { + for (Method method : entry.getValue()) { + try { + method.invoke(entry.getKey().fListener, new Object[] { event } ); + } + catch (IllegalAccessException e) { + DsfPlugin.getDefault().getLog().log(new Status( + IStatus.ERROR, DsfPlugin.PLUGIN_ID, -1, "Security exception when calling a service event handler method", e)); //$NON-NLS-1$ + assert false : "IServiceEventListener.ServiceHandlerMethod method not accessible, is listener declared public?"; //$NON-NLS-1$ + } + catch (InvocationTargetException e) { + DsfPlugin.getDefault().getLog().log(new Status( + IStatus.ERROR, DsfPlugin.PLUGIN_ID, -1, "Invocation exception when calling a service event handler method", e)); //$NON-NLS-1$ + assert false : "Exception thrown by a IServiceEventListener.ServiceHandlerMethod method"; //$NON-NLS-1$ + } + } + } + } + + private Method[] getEventHandlerMethods(Object listener) + { + List<Method> retVal = new ArrayList<Method>(); + try { + Method[] methods = listener.getClass().getMethods(); + for (Method method : methods) { + if (method.isAnnotationPresent(DsfServiceEventHandler.class)) { + Class<?>[] paramTypes = method.getParameterTypes(); + if (paramTypes.length > 2) { + throw new IllegalArgumentException("ServiceEventHandler method has incorrect number of parameters"); //$NON-NLS-1$ + } + retVal.add(method); + } + } + } catch(SecurityException e) { + throw new IllegalArgumentException("No permission to access ServiceEventHandler method"); //$NON-NLS-1$ + } + + if (retVal.isEmpty()) { + throw new IllegalArgumentException("No methods marked with @ServiceEventHandler in listener, is listener declared public?"); //$NON-NLS-1$ + } + return retVal.toArray(new Method[retVal.size()]); + } + + /** + * Class to be instanciated only using startSession() + */ + @ThreadSafe + private DsfSession(DsfExecutor executor, String ownerId, String id) { + fId = id; + fOwnerId = ownerId; + fExecutor = executor; + } + +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/IDsfService.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/IDsfService.java new file mode 100644 index 00000000000..f0181ea115b --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/IDsfService.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.dsf.service; + +import java.util.Dictionary; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; + +/** + * The inteface that all DSF services must implement. It only privides a + * few features to help manage and identify the servies using the OSGI services + * framework. + * <p> + * Each service should register itself with OSGI services framework using + * the BundleContext.registerService() method. And each service should use the + * session ID that it is registering with as one of the service properties. If there + * is more than one instance of the service to be instanciated for a given session, + * additional properties should be used when registering the service to allow clients + * to uniquely identify the services. + * <p> + * By convention, all methods of DSF services can be called only on the dispatch + * thread of the DSF executor that is associated with the service. If a + * service exposes a method that is to be called on non-dispatch thread, it should + * be documented so. + * + * @see org.osgi.framework.BundleContext#registerService(String[], Object, Dictionary) + */ +@ConfinedToDsfExecutor("getExecutor") +public interface IDsfService { + + /** + * Property name for the session-id of this service. This property should be set by + * all DSF services when they are registered with OSGI service framework. + */ + final static String PROP_SESSION_ID = "org.eclipse.cdt.dsf.service.IService.session_id"; //$NON-NLS-1$ + + /** + * Returns the DSF Session that this service belongs to. + */ + DsfSession getSession(); + + /** + * Returns the executor that should be used to call methods of this service. + * This method is equivalent to calling getSession().getExecutor() + */ + DsfExecutor getExecutor(); + + /** + * Returns the map of properties that this service was registered with. + */ + @SuppressWarnings("unchecked") + Dictionary getProperties(); + + /** + * Returns a filter string that can be used to uniquely identify this + * service. This filter string should be based on the properties and class + * name, which were used to register this service. + * @see org.osgi.framework.BundleContext#getServiceReferences + */ + String getServiceFilter(); + + /** + * Performs initialization and registration of the given service. Implementation + * should initialize the service, so that all methods and events belonging to this + * service can be used following the initialization. + * <br>Note: Since service initializaiton should be performed by an external + * logic, if this service depends on other services, the implementaion should + * assume that these services are already present, and if they are not, the + * initializaiton should fail. + * @param requestMonitor callback to be submitted when the initialization is complete + */ + void initialize(RequestMonitor requestMonitor); + + /** + * Performs shutdown and de-registration of the given service. + * @param requestMonitor callback to be submitted when shutdown is complete + */ + void shutdown(RequestMonitor requestMonitor); + + /** + * Returns the startup order number of this service among services in the same session. + * Implementations should get this number during initialization by calling + * Session.getAndIncrementServiceStartupCounter(). This counter is used to Session + * objects to prioritize the listeners of service events. + * @return startup order number of this service + * @see org.eclipse.cdt.dsf.service.DsfSession#getAndIncrementServiceStartupCounter() + */ + int getStartupNumber(); +} |