diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/.options b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/.options
new file mode 100644
index 00000000000..033a48e511e
--- /dev/null
+++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/.options
@@ -0,0 +1,2 @@
+org.eclipse.cdt.tests.dsf.gdb/debug = false
+org.eclipse.cdt.tests.dsf.gdb/debug/waitMetrics = false
diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/ServiceEventWaitor.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/ServiceEventWaitor.java
index f1e2f3b73ed..2edc1a6ad16 100644
--- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/ServiceEventWaitor.java
+++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/ServiceEventWaitor.java
@@ -12,17 +12,20 @@ package org.eclipse.cdt.tests.dsf.gdb.framework;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin;
+import org.eclipse.core.runtime.Platform;
/*
* This class provides a way to wait for an asynchronous ServerEvent
* to occur. The user of this class specifies which event is of
- * interest using the proper constructor or the registerForEvent() method.
- * waitForEvent() can then be called to block until the event occurs or
- * the timeout elapses.
+ * interest . waitForEvent() can then be called to block until the event occurs or
+ * the timeout elapses. It's important that this object be created before
+ * executing the debugger operation that will cause the expected event to occur,
+ * otherwise the caller stands to miss out on the event.
*
- * Note that if the event occurs after regsiterForEvent() is called but
- * before waitForEvent() is called, waitForEvent() will return immediatly
- * since it will know the event has already occured.
+ * Note that if the event occurs after object construction but
+ * before waitForEvent() is called, waitForEvent() will return immediately
+ * since it will know the event has already occurred.
*/
public class ServiceEventWaitor {
@@ -36,32 +39,28 @@ public class ServiceEventWaitor {
private Class fEventTypeClass;
private DsfSession fSession;
private V fEvent;
-
+
+ /**
+ * Trace option for wait metrics
+ */
+ private static final boolean LOG = TestsPlugin.DEBUG && "true".equals(Platform.getDebugOption("org.eclipse.cdt.tests.dsf.gdb/debug/waitMetrics")); //$NON-NLS-1$//$NON-NLS-2$
- /* Empty contructor. registerForEvent() should be called when
- * this constructor is used.
- */
- public ServiceEventWaitor(DsfSession session) {
- fSession = session;
- }
-
- /* Contructor that takes the eventClass as parameter. This is a shortcut
- * that avoids calling registerForEvent()
+ /**
+ * Constructor
+ *
+ * @param session
+ * the DSF session we'll wait for an event to happen on
+ * @param eventClass
+ * the event to expect
*/
public ServiceEventWaitor(DsfSession session, Class eventClass) {
- this(session);
- registerForEvent(eventClass);
- }
-
- /* Specify which event to wait for, and add ourselves as
- * a listener with the session
- */
- public void registerForEvent(Class eventClass) {
+ assert eventClass != null;
+ fSession = session;
fEventTypeClass = eventClass;
fEvent = null;
fSession.addServiceEventListener(this, null);
}
-
+
@Override
protected void finalize() throws Throwable {
super.finalize();
@@ -69,9 +68,8 @@ public class ServiceEventWaitor {
}
/*
- * Block until 'timeout' or the previously specified event has been
- * received. The reason we don's specify the event as a parameter is that we
- * must be ready for the event to occur even before this method is called.
+ * Block until 'timeout' or the expected event occurs. The expected event is
+ * specified at construction time.
*
* @param timeout the maximum time to wait in milliseconds.
*/
@@ -79,20 +77,61 @@ public class ServiceEventWaitor {
if (fEventTypeClass == null) {
throw new Exception("Event to wait for has not been specified!");
}
+
+ long startMs = System.currentTimeMillis();
+
// The event might have already been received
- if (fEvent != null) return fEvent;
-
- wait(timeout);
-
if (fEvent == null) {
- throw new Exception("Timed out waiting for ServiceEvent: " + fEventTypeClass.getName());
+ wait(timeout);
+ if (fEvent == null) {
+ throw new Exception("Timed out waiting for ServiceEvent: " + fEventTypeClass.getName());
+ }
}
+
+ long stopMs = System.currentTimeMillis();
+
+ // Turning on trace during development gives you the following
+ // helpful analysis, which you can use to establish reasonable timeouts,
+ // and detect poorly configured ones. The best way to use this it to
+ // set breakpoints on the WARNING println calls.
+ if (LOG) {
+ final long duration = stopMs - startMs;
+ System.out.println("The following caller waited for " + (duration) + " milliseconds");
+ boolean print = false;
+ for (StackTraceElement frame : Thread.currentThread().getStackTrace()) {
+ if (frame.toString().startsWith("sun.reflect.NativeMethodAccessorImpl")) {
+ // ignore anything once we get into the reflection/junit portion of the stack
+ System.out.println("\t... (junit)");
+ break;
+ }
+ if (print) {
+ System.out.println("\t" + frame);
+ }
+ if (!print && frame.toString().contains("ServiceEventWaitor.waitForEvent")) {
+ // we're only interested in the call stack up to (and including) our caller
+ print = true;
+ }
+ }
+
+ if (timeout != WAIT_FOREVER) {
+ if (timeout/duration > 7.0) {
+ System.out.println("WARNING: Caller specified a timeout that was more than 7X what was necessary. The timeout is probably too loose.");
+ }
+ else if ((((float)(timeout - duration))/(float)duration) < 0.20) {
+ System.out.println("WARNING: Caller specified a timeout that was less than 20% above actual time. The timeout is probably too tight.");
+ }
+ }
+ else {
+ System.out.println("WARNING: Caller requested to wait forever. It should probably specify some reasonable value.");
+ }
+ }
+
return fEvent;
}
/*
* Listen to all possible events by having the base class be the parameter.
- * and then igure out if that event is the one we were waiting for.
+ * and then figure out if that event is the one we were waiting for.
*/
@DsfServiceEventHandler
public void eventDispatched(V event) {
diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/launching/TestsPlugin.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/launching/TestsPlugin.java
index 7c3aa379ca0..335acc159da 100644
--- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/launching/TestsPlugin.java
+++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/launching/TestsPlugin.java
@@ -13,6 +13,7 @@ package org.eclipse.cdt.tests.dsf.gdb.launching;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
+import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Plugin;
import org.osgi.framework.BundleContext;
@@ -28,6 +29,9 @@ public class TestsPlugin extends Plugin {
public static final String PLUGIN_ID = "org.eclipse.cdt.tests.dsf.gdb"; //$NON-NLS-1$
+ /** Base tracing option for this plugin */
+ public static final boolean DEBUG = "true".equals(Platform.getDebugOption("org.eclipse.cdt.tests.dsf.gdb/debug")); //$NON-NLS-1$//$NON-NLS-2$
+
/**
* The constructor.
*/