Revision: 10159
Author: [email protected]
Date: Fri May 6 10:22:03 2011
Log: Phase 1 of GWT Dashboard. This includes an interface in gwt-dev
for posting to a dashboard (default implementation is a no-op). Added calls
to this interface in a couple of places. Created factory class that can
provide the no-op implementation or a real implementation (based on system
property). Updated unit test for SpeedTracerLogger to include a test that
verifies that it is posting events to dashboard as expected.
Review at http://gwt-code-reviews.appspot.com/1427807
Review by: [email protected]
http://code.google.com/p/google-web-toolkit/source/detail?r=10159
Added:
/trunk/dev/core/src/com/google/gwt/dev/shell/DevModeSession.java
/trunk/dev/core/src/com/google/gwt/dev/util/log/dashboard
/trunk/dev/core/src/com/google/gwt/dev/util/log/dashboard/DashboardNotifier.java
/trunk/dev/core/src/com/google/gwt/dev/util/log/dashboard/DashboardNotifierFactory.java
/trunk/dev/core/src/com/google/gwt/dev/util/log/dashboard/NoOpDashboardNotifier.java
/trunk/dev/core/test/com/google/gwt/dev/shell/DevModeSessionTest.java
/trunk/dev/core/test/com/google/gwt/dev/shell/DevModeSessionTestUtil.java
/trunk/dev/core/test/com/google/gwt/dev/util/log/dashboard
/trunk/dev/core/test/com/google/gwt/dev/util/log/dashboard/DashboardNotifierFactoryTest.java
/trunk/dev/core/test/com/google/gwt/dev/util/log/dashboard/SpeedTracerLoggerTestMockNotifier.java
Modified:
/trunk/dev/core/src/com/google/gwt/dev/shell/BrowserChannelServer.java
/trunk/dev/core/src/com/google/gwt/dev/shell/OophmSessionHandler.java
/trunk/dev/core/src/com/google/gwt/dev/util/log/speedtracer/SpeedTracerLogger.java
/trunk/dev/core/test/com/google/gwt/dev/shell/BrowserChannelServerTest.java
/trunk/dev/core/test/com/google/gwt/dev/util/log/speedtracer/SpeedTracerLoggerTest.java
=======================================
--- /dev/null
+++ /trunk/dev/core/src/com/google/gwt/dev/shell/DevModeSession.java Fri
May 6 10:22:03 2011
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
not
+ * use this file except in compliance with the License. You may obtain a
copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+
+package com.google.gwt.dev.shell;
+
+/**
+ * Represents a session between devmode and a browser plugin. A session is
+ * essentially a socket connection, established by the plugin when a GWT
module
+ * is started in devmode and used to communicate invocations of Java from
+ * JavaScript and vice-versa.
+ */
+public class DevModeSession {
+
+ private static final ThreadLocal<DevModeSession> sessionForCurrentThread
=
+ new ThreadLocal<DevModeSession>();
+
+ /**
+ * Gets the devmode session for the current thread. If a thread is not
+ * associated with a devmode session, via
+ * <code>setSessionForCurrentThread()</code>, then this will return null.
+ */
+ public static DevModeSession getSessionForCurrentThread() {
+ return sessionForCurrentThread.get();
+ }
+
+ /**
+ * Sets the devmode session for the current thread.
+ */
+ static void setSessionForCurrentThread(DevModeSession session) {
+ sessionForCurrentThread.set(session);
+ }
+
+ private String moduleName;
+ private String userAgent;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param moduleName the name of the GWT module for this session
+ * @param userAgent the User agent field provided by the browser for this
+ * session
+ */
+ DevModeSession(String moduleName, String userAgent) {
+ this.moduleName = moduleName;
+ this.userAgent = userAgent;
+ }
+
+ public String getModuleName() {
+ return moduleName;
+ }
+
+ public String getUserAgent() {
+ return userAgent;
+ }
+}
=======================================
--- /dev/null
+++
/trunk/dev/core/src/com/google/gwt/dev/util/log/dashboard/DashboardNotifier.java
Fri May 6 10:22:03 2011
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
not
+ * use this file except in compliance with the License. You may obtain a
copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+
+package com.google.gwt.dev.util.log.dashboard;
+
+import com.google.gwt.dev.shell.DevModeSession;
+import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
+
+/**
+ * Sends information to a dashboard service. The dashboard service collects
+ * information from GWT runtime and compiler instrumentation.
+ */
+public interface DashboardNotifier {
+ // First: Compiler related entry points
+
+ // TODO(jhumphries) Add interface methods for collecting data from the
+ // compiler
+
+ // Second: Dev-Mode related entry points
+
+ /**
+ * Records a top-level event to the dashboard.
+ */
+ void devModeEvent(DevModeSession session, Event event);
+
+ /**
+ * Records a new module/session.
+ */
+ void devModeSession(DevModeSession session);
+
+ // Third: Test related entry points
+
+ // TODO(jhumphries) Add interface methods for collecting data from
automated
+ // tests
+
+}
=======================================
--- /dev/null
+++
/trunk/dev/core/src/com/google/gwt/dev/util/log/dashboard/DashboardNotifierFactory.java
Fri May 6 10:22:03 2011
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
not
+ * use this file except in compliance with the License. You may obtain a
copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+
+package com.google.gwt.dev.util.log.dashboard;
+
+/**
+ * Gets an instance of {@link DashboardNotifier} for sending data to a GWT
+ * dashboard.
+ */
+public class DashboardNotifierFactory {
+ private static final NoOpDashboardNotifier defaultNotifier = new
NoOpDashboardNotifier();
+ private static DashboardNotifier theNotifier;
+
+ static {
+
setNotifier(createNotifier(System.getProperty("gwt.dashboard.notifierClass")));
+ }
+
+ /**
+ * Determines whether notifications to a GWT Dashboard are enabled.
Returns
+ * true if the current notifier is <strong>not</strong> the
default "no-op"
+ * instance.
+ */
+ public static boolean areNotificationsEnabled() {
+ return theNotifier != defaultNotifier;
+ }
+
+ /**
+ * Returns an instance of {@code DashboardNotifier} for sending data to
a GWT
+ * dashboard.
+ */
+ public static DashboardNotifier getNotifier() {
+ return theNotifier;
+ }
+
+ /**
+ * Creates a {@code DashboardNotifier} from a given class name. The
object is
+ * instantiated via reflection.
+ *
+ * @return the new notifier instance if the creation was successful or
null on
+ * failure
+ */
+ static DashboardNotifier createNotifier(String className) {
+ // Create the instance!
+ DashboardNotifier notifier = null;
+ if (className != null) {
+ try {
+ Class<?> clazz = Class.forName(className);
+ notifier = (DashboardNotifier) clazz.newInstance();
+ } catch (Exception e) {
+ // print error and skip dashboard notifications...
+ new Exception("Unexpected failure while trying to load dashboard
class: " + className
+ + ". Notifications to the dashboard will be disabled.",
e).printStackTrace();
+ return null;
+ }
+ }
+ return notifier;
+ }
+
+ /**
+ * Defines the {@code DashboardNotifier} returned by this factory.
Exposed for
+ * unit testing purposes (to support mock notifier objects). If set to
null, a
+ * default notifier (whose methods do nothing) will be returned by
subsequent
+ * calls to {@code getNotifier}, not null.
+ */
+ static void setNotifier(DashboardNotifier notifier) {
+ theNotifier = notifier == null ? defaultNotifier : notifier;
+ }
+
+ /**
+ * Prevents this class from being instantiated. Instead use static method
+ * {@link #getNotifier()}.
+ */
+ private DashboardNotifierFactory() {
+ }
+}
=======================================
--- /dev/null
+++
/trunk/dev/core/src/com/google/gwt/dev/util/log/dashboard/NoOpDashboardNotifier.java
Fri May 6 10:22:03 2011
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
not
+ * use this file except in compliance with the License. You may obtain a
copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+
+package com.google.gwt.dev.util.log.dashboard;
+
+import com.google.gwt.dev.shell.DevModeSession;
+import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
+
+/**
+ * Implements the <code>DashboardNotifier</code> interface but does not
actually
+ * do anything.
+ */
+public class NoOpDashboardNotifier implements DashboardNotifier {
+
+ @Override
+ public void devModeEvent(DevModeSession sesion, Event event) {
+ // do nothing
+ }
+
+ @Override
+ public void devModeSession(DevModeSession session) {
+ // do nothing
+ }
+
+}
=======================================
--- /dev/null
+++ /trunk/dev/core/test/com/google/gwt/dev/shell/DevModeSessionTest.java
Fri May 6 10:22:03 2011
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
not
+ * use this file except in compliance with the License. You may obtain a
copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+package com.google.gwt.dev.shell;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests the DevModeSession class.
+ */
+public class DevModeSessionTest extends TestCase {
+ public void testConstructor() {
+ String moduleName = "test module name";
+ String userAgent = "test user agent";
+ DevModeSession session = new DevModeSession(moduleName, userAgent);
+ assertEquals("Constructor failed to initialize moduleName",
session.getModuleName(), moduleName);
+ assertEquals("Constructor failed to initialize userAgent",
session.getUserAgent(), userAgent);
+ }
+
+ public void testSetSessionForCurrentThread() {
+ DevModeSession session = new DevModeSession("test", "test");
+ // call method
+ DevModeSession.setSessionForCurrentThread(session);
+ // verify
+ assertTrue(DevModeSession.getSessionForCurrentThread() == session);
+ // tear-down
+ DevModeSession.setSessionForCurrentThread(null);
+ }
+}
=======================================
--- /dev/null
+++
/trunk/dev/core/test/com/google/gwt/dev/shell/DevModeSessionTestUtil.java
Fri May 6 10:22:03 2011
@@ -0,0 +1,20 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+package com.google.gwt.dev.shell;
+
+/**
+ * Provides public method for accessing package-private members of
+ * <code>DevModeSession</code> for testing.
+ *
+ * @author [email protected] (Joshua Humphries)
+ */
+public class DevModeSessionTestUtil {
+ public static DevModeSession createSession(String moduleName, String
userAgent,
+ boolean setForCurrentThread) {
+ DevModeSession session = new DevModeSession(moduleName, userAgent);
+ if (setForCurrentThread) {
+ DevModeSession.setSessionForCurrentThread(session);
+ }
+ return session;
+ }
+}
=======================================
--- /dev/null
+++
/trunk/dev/core/test/com/google/gwt/dev/util/log/dashboard/DashboardNotifierFactoryTest.java
Fri May 6 10:22:03 2011
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
not
+ * use this file except in compliance with the License. You may obtain a
copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+package com.google.gwt.dev.util.log.dashboard;
+
+import com.google.gwt.dev.shell.DevModeSession;
+import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests the DashboardNotifierFactory class.
+ */
+public class DashboardNotifierFactoryTest extends TestCase {
+
+ public void testSetNotifier() {
+ // create test notifier instance
+ DashboardNotifier obj = new DashboardNotifier() {
+ @Override
+ public void devModeEvent(DevModeSession session, Event event) {
+ // no need to do anything
+ }
+
+ @Override
+ public void devModeSession(DevModeSession session) {
+ // no need to do anything
+ }
+ };
+ // call method
+ DashboardNotifierFactory.setNotifier(obj);
+ // verify it worked
+ assertTrue("Notifier is not set correctly!",
DashboardNotifierFactory.getNotifier() == obj);
+ assertTrue("Setting notifier failed to enable notifications!",
DashboardNotifierFactory
+ .areNotificationsEnabled());
+ }
+
+ public void testClearNotifier() {
+ // clearing the notifier should use a "no-op" instance and disable
+ // notifications
+ DashboardNotifierFactory.setNotifier(null);
+ // verify it worked
+ assertTrue("Notifier is of wrong type!",
+ DashboardNotifierFactory.getNotifier() instanceof
NoOpDashboardNotifier);
+ assertFalse("Resetting notifier failed to disable notifications!",
DashboardNotifierFactory
+ .areNotificationsEnabled());
+ }
+
+ public void testCreateNotifier() {
+ DashboardNotifier notifier =
+
DashboardNotifierFactory.createNotifier(SpeedTracerLoggerTestMockNotifier.class.getName());
+ assertNotNull("Notifier could not be created!", notifier);
+ assertTrue(notifier instanceof SpeedTracerLoggerTestMockNotifier);
+ }
+
+ public void testCreateNotifierBadClass() {
+
assertNull(DashboardNotifierFactory.createNotifier("this.is.not.a.valid.Notifier"));
+ }
+}
=======================================
--- /dev/null
+++
/trunk/dev/core/test/com/google/gwt/dev/util/log/dashboard/SpeedTracerLoggerTestMockNotifier.java
Fri May 6 10:22:03 2011
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
not
+ * use this file except in compliance with the License. You may obtain a
copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+package com.google.gwt.dev.util.log.dashboard;
+
+import com.google.gwt.dev.shell.DevModeSession;
+import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
+
+import junit.framework.Assert;
+
+import java.util.LinkedList;
+
+/**
+ * Mock object for testing SpeedTracerLogger
+ */
+public class SpeedTracerLoggerTestMockNotifier implements
DashboardNotifier {
+ /**
+ * Activates this mock object. After calling this, the notifier factory
will
+ * be setup so that dashboard notifications are enabled and the notifier
+ * instance returned is an instance of this class
+ */
+ public static SpeedTracerLoggerTestMockNotifier enable() {
+ SpeedTracerLoggerTestMockNotifier ret = new
SpeedTracerLoggerTestMockNotifier();
+ DashboardNotifierFactory.setNotifier(ret);
+ return ret;
+ }
+
+ /**
+ * This keeps track of events
+ */
+ private LinkedList<Event> eventSeq = new LinkedList<Event>();
+
+ @Override
+ public void devModeEvent(DevModeSession session, Event event) {
+ eventSeq.add(event);
+ }
+
+ @Override
+ public void devModeSession(DevModeSession session) {
+ // always raise exception here - this method shouldn't be invoked from
+ // SpeedTracerLogger
+ Assert.assertTrue("SpeedTracerLogger should not be calling
DashboardNotifier.devModeSession()",
+ false);
+ }
+
+ public LinkedList<Event> getEventSequence() {
+ return eventSeq;
+ }
+}
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/shell/BrowserChannelServer.java
Tue Aug 3 17:01:46 2010
+++ /trunk/dev/core/src/com/google/gwt/dev/shell/BrowserChannelServer.java
Fri May 6 10:22:03 2011
@@ -19,6 +19,7 @@
import com.google.gwt.core.ext.TreeLogger.HelpInfo;
import
com.google.gwt.dev.shell.BrowserChannel.SessionHandler.ExceptionOrReturnValue;
import com.google.gwt.dev.shell.JsValue.DispatchObject;
+import com.google.gwt.dev.util.log.dashboard.DashboardNotifierFactory;
import java.io.IOException;
import java.io.InputStream;
@@ -106,6 +107,8 @@
private static Map<String, byte[]> iconCache = new HashMap<String,
byte[]>();
private static final Object cacheLock = new Object();
+
+ private DevModeSession devModeSession;
private final SessionHandlerServer handler;
@@ -162,6 +165,13 @@
throw new HostedModeException("I/O error communicating with client");
}
}
+
+ /**
+ * Returns the {@code DevModeSession} representing this browser
connection.
+ */
+ public DevModeSession getDevModeSession() {
+ return devModeSession;
+ }
/**
* @return the table of Java objects which have been sent to the browser.
@@ -507,6 +517,9 @@
Thread.currentThread().setName(
"Code server for " + moduleName + " from " + userAgent + " on " +
url
+ " @ " + sessionKey);
+
+ createDevModeSession();
+
logger = handler.loadModule(this, moduleName, userAgent, url,
tabKey, sessionKey, iconBytes);
if (logger == null) {
@@ -631,6 +644,17 @@
break;
}
}
+
+ /**
+ * Creates the {@code DevModeSession} that represents the current browser
+ * connection, sets it as the "default" session for the current thread,
and
+ * notifies a GWT Dashboard.
+ */
+ private void createDevModeSession() {
+ devModeSession = new DevModeSession(moduleName, userAgent);
+ DevModeSession.setSessionForCurrentThread(devModeSession);
+ DashboardNotifierFactory.getNotifier().devModeSession(devModeSession);
+ }
/**
* Create the requested transport and return the appropriate information
so
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/shell/OophmSessionHandler.java
Tue Apr 26 08:02:24 2011
+++ /trunk/dev/core/src/com/google/gwt/dev/shell/OophmSessionHandler.java
Fri May 6 10:22:03 2011
@@ -101,7 +101,8 @@
@Override
public ExceptionOrReturnValue invoke(BrowserChannelServer channel,
Value thisVal, int methodDispatchId, Value[] args) {
- Event jsToJavaCallEvent =
SpeedTracerLogger.start(DevModeEventType.JS_TO_JAVA_CALL);
+ Event jsToJavaCallEvent =
+ SpeedTracerLogger.start(channel.getDevModeSession(),
DevModeEventType.JS_TO_JAVA_CALL);
ServerObjectsTable localObjects =
channel.getJavaObjectsExposedInBrowser();
ModuleSpace moduleSpace = moduleMap.get(channel);
ModuleHandle moduleHandle = moduleHandleMap.get(channel);
@@ -183,7 +184,9 @@
public synchronized TreeLogger loadModule(BrowserChannelServer channel,
String moduleName, String userAgent, String url, String tabKey,
String sessionKey, byte[] userAgentIcon) {
- Event moduleInit =
SpeedTracerLogger.start(DevModeEventType.MODULE_INIT, "Module Name",
moduleName);
+ Event moduleInit =
+ SpeedTracerLogger.start(channel.getDevModeSession(),
DevModeEventType.MODULE_INIT,
+ "Module Name", moduleName);
ModuleHandle moduleHandle = host.createModuleLogger(moduleName,
userAgent,
url, tabKey, sessionKey, channel, userAgentIcon);
TreeLogger logger = moduleHandle.getLogger();
=======================================
---
/trunk/dev/core/src/com/google/gwt/dev/util/log/speedtracer/SpeedTracerLogger.java
Mon Mar 7 09:15:21 2011
+++
/trunk/dev/core/src/com/google/gwt/dev/util/log/speedtracer/SpeedTracerLogger.java
Fri May 6 10:22:03 2011
@@ -17,7 +17,9 @@
import com.google.gwt.dev.json.JsonArray;
import com.google.gwt.dev.json.JsonObject;
+import com.google.gwt.dev.shell.DevModeSession;
import com.google.gwt.dev.util.collect.Lists;
+import com.google.gwt.dev.util.log.dashboard.DashboardNotifierFactory;
import java.io.BufferedWriter;
import java.io.FileWriter;
@@ -60,13 +62,14 @@
private static final String defaultFormatString =
System.getProperty("gwt.speedtracerformat");
- // Use cummulative multi-threaded process cpu time instead of wall time
+ // Use cumulative multi-threaded process cpu time instead of wall time
private static final boolean logProcessCpuTime =
getBooleanProperty("gwt.speedtracer.logProcessCpuTime");
- // Use per thread cpu time instead of wall time
- private static final boolean logThreadCpuTime =
- getBooleanProperty("gwt.speedtracer.logThreadCpuTime");
+ // Use per thread cpu time instead of wall time. If logProcessCpuTime is
set,
+ // then this can remain false - we only need one or the other.
+ private static final boolean logThreadCpuTime =
+ getBooleanProperty("gwt.speedtracer.logThreadCpuTime");
// Turn on logging summarizing gc time during an event
private static final boolean logGcTime =
@@ -81,6 +84,15 @@
private static final boolean jsniCallLoggingEnabled =
!getBooleanProperty("gwt.speedtracer.disableJsniLogging");
+ static {
+ // verify configuration
+ if (logProcessCpuTime && logThreadCpuTime) {
+ throw new RuntimeException("System properties are misconfigured: "
+ + "Specify one or the other
of 'gwt.speedtracer.logProcessCpuTime' "
+ + "or 'gwt.speedtracer.logThreadCpuTime', not both.");
+ }
+ }
+
/**
* Represents a node in a tree of SpeedTracer events.
*/
@@ -88,38 +100,46 @@
protected final EventType type;
List<Event> children;
List<String> data;
- long durationNanos;
- final long startTimeNanos;
+ DevModeSession devModeSession;
+
+ long elapsedDurationNanos;
+ long elapsedStartTimeNanos;
+
+ long processCpuDurationNanos;
+ long processCpuStartTimeNanos;
+
+ long threadCpuDurationNanos;
+ long threadCpuStartTimeNanos;
Event() {
if (enabled) {
- timeKeeper.resetTimeBase();
- this.startTimeNanos = timeKeeper.normalizedTimeNanos();
+ threadCpuTimeKeeper.resetTimeBase();
+ recordStartTime();
this.data = Lists.create();
this.children = Lists.create();
} else {
- this.startTimeNanos = 0L;
+ this.processCpuStartTimeNanos = 0L;
+ this.threadCpuStartTimeNanos = 0L;
+ this.elapsedStartTimeNanos = 0L;
this.data = null;
this.children = null;
}
this.type = null;
}
-
- Event(Event parent, EventType type, String... data) {
- this(timeKeeper.normalizedTimeNanos(), parent, type, data);
- }
-
- Event(long startTimeNanos, Event parent, EventType type, String...
data) {
+
+ Event(DevModeSession session, Event parent, EventType type, String...
data) {
+
if (parent != null) {
parent.children = Lists.add(parent.children, this);
}
this.type = type;
assert (data.length % 2 == 0);
+ recordStartTime();
this.data = Lists.create(data);
this.children = Lists.create();
- this.startTimeNanos = startTimeNanos;
- }
-
+ this.devModeSession = session;
+ }
+
/**
* @param data key/value pairs to add to JSON object.
*/
@@ -129,27 +149,84 @@
this.data = Lists.addAll(this.data, data);
}
}
-
+
/**
* Signals the end of the current event.
*/
public void end(String... data) {
endImpl(this, data);
}
+
+ public DevModeSession getDevModeSession() {
+ return devModeSession;
+ }
+
+ /**
+ * Returns the event duration, in nanoseconds, for the log file.
Depending
+ * on system properties, this will measured in elapsed time, process
CPU
+ * time, or thread CPU time.
+ */
+ public long getDurationNanos() {
+ return logProcessCpuTime ? processCpuDurationNanos :
(logThreadCpuTime
+ ? threadCpuDurationNanos : elapsedDurationNanos);
+ }
+
+ public long getElapsedDurationNanos() {
+ return this.elapsedDurationNanos;
+ }
+
+ public long getElapsedStartTimeNanos() {
+ return this.elapsedStartTimeNanos;
+ }
+
+ /**
+ * Returns the event start time, normalized in nanoseconds, for the log
+ * file. Depending on system properties, this will be normalized based
on
+ * elapsed time, process CPU time, or thread CPU time.
+ */
+ public long getStartTimeNanos() {
+ return logProcessCpuTime ? processCpuStartTimeNanos :
(logThreadCpuTime
+ ? threadCpuStartTimeNanos : elapsedStartTimeNanos);
+ }
+
+ public EventType getType() {
+ return type;
+ }
@Override
public String toString() {
return type.getName();
}
+ /**
+ * Extends the durations of the current event by the durations of the
+ * specified event.
+ */
+ void extendDuration(Event refEvent) {
+ elapsedDurationNanos += refEvent.elapsedDurationNanos;
+ processCpuDurationNanos += refEvent.processCpuDurationNanos;
+ threadCpuDurationNanos += refEvent.threadCpuDurationNanos;
+ }
+
+ /**
+ * Sets the start time of this event to start immediately after the
+ * specified event ends.
+ */
+ void setStartsAfter(Event refEvent) {
+ elapsedStartTimeNanos = refEvent.elapsedStartTimeNanos +
refEvent.elapsedDurationNanos;
+ processCpuStartTimeNanos =
+ refEvent.processCpuStartTimeNanos +
refEvent.processCpuDurationNanos;
+ threadCpuStartTimeNanos = refEvent.threadCpuStartTimeNanos +
refEvent.threadCpuDurationNanos;
+ }
+
JsonObject toJson() {
JsonObject json = JsonObject.create();
json.put("type", -2);
json.put("typeName", type.getName());
json.put("color", type.getColor());
- double startMs = convertToMilliseconds(startTimeNanos);
+ double startMs = convertToMilliseconds(getStartTimeNanos());
json.put("time", startMs);
- double durationMs = convertToMilliseconds(durationNanos);
+ double durationMs = convertToMilliseconds(getDurationNanos());
json.put("duration", durationMs);
JsonObject jsonData = JsonObject.create();
@@ -166,6 +243,49 @@
return json;
}
+
+ /**
+ * Records the duration of this event based on the current time and the
+ * event's recorded start time.
+ */
+ void updateDuration() {
+ long elapsedEndTimeNanos = elapsedTimeKeeper.normalizedTimeNanos();
+ assert (elapsedEndTimeNanos >= elapsedStartTimeNanos);
+ elapsedDurationNanos = elapsedEndTimeNanos - elapsedStartTimeNanos;
+
+ // don't bother making expensive time keeping method calls unless
+ // necessary
+ if (logProcessCpuTime) {
+ long processCpuEndTimeNanos =
processCpuTimeKeeper.normalizedTimeNanos();
+ assert (processCpuEndTimeNanos >= processCpuStartTimeNanos);
+ processCpuDurationNanos = processCpuEndTimeNanos -
processCpuStartTimeNanos;
+ } else if (logThreadCpuTime) {
+ long threadCpuEndTimeNanos =
threadCpuTimeKeeper.normalizedTimeNanos();
+ assert (threadCpuEndTimeNanos >= threadCpuStartTimeNanos);
+ threadCpuDurationNanos = threadCpuEndTimeNanos -
threadCpuStartTimeNanos;
+ }
+ }
+
+ /**
+ * Marks the start time for this event. Three different time
measurements
+ * are used:
+ * <ol>
+ * <li>Elapsed (wall-clock) time</li>
+ * <li>Process CPU time</li>
+ * <li>Thread CPU time</li>
+ * </ol>
+ */
+ private void recordStartTime() {
+ elapsedStartTimeNanos = elapsedTimeKeeper.normalizedTimeNanos();
+
+ // don't bother making expensive time keeping method calls unless
+ // necessary
+ if (logProcessCpuTime) {
+ processCpuStartTimeNanos =
processCpuTimeKeeper.normalizedTimeNanos();
+ } else if (logThreadCpuTime) {
+ threadCpuStartTimeNanos =
threadCpuTimeKeeper.normalizedTimeNanos();
+ }
+ }
}
/**
@@ -210,21 +330,68 @@
}
}
- private interface NormalizedTimeKeeper {
- long normalizedTimeNanos();
- void resetTimeBase();
- long zeroTimeMillis();
- }
-
- /*
+ /**
+ * Provides functionality specific to garbage collection events.
+ */
+ private class GcEvent extends Event {
+ private Event refEvent;
+
+ /**
+ * Constructs an event that represents garbage collection metrics.
+ *
+ * @param refEvent the event during which the garbage collections took
place
+ * @param gcType the garbage collector type
+ * @param collectionCount the total number of collections for this
garbage
+ * collector type
+ * @param durationNanos the total elapsed time spent in garbage
collection
+ * during the span of {@code refEvent}
+ */
+ GcEvent(Event refEvent, String gcType, long collectionCount, long
durationNanos) {
+ super(null, null, SpeedTracerEventType.GC, "Collector Type", gcType,
+ "Cumulative Collection Count", Long.toString(collectionCount));
+
+ this.refEvent = refEvent;
+ // GarbageCollectorMXBean can only provide elapsed time, so that's
all we
+ // record
+ this.elapsedDurationNanos = durationNanos;
+ }
+
+ /**
+ * Returns elapsed duration since that is the only duration we can
measure
+ * for garbage collection events.
+ */
+ @Override
+ public long getDurationNanos() {
+ return getElapsedDurationNanos();
+ }
+
+ /**
+ * Returns a start time so that this event ends with its {@code
refEvent}.
+ */
+ @Override
+ public long getElapsedStartTimeNanos() {
+ return refEvent.getElapsedStartTimeNanos() +
refEvent.getElapsedDurationNanos()
+ - getElapsedDurationNanos();
+ }
+
+ /**
+ * Returns a start time so that this event ends with its {@code
refEvent}.
+ */
+ @Override
+ public long getStartTimeNanos() {
+ return refEvent.getStartTimeNanos() + refEvent.getDurationNanos() -
getDurationNanos();
+ }
+ }
+
+ /**
* Time keeper which uses wall time.
*/
- private class DefaultNormalizedTimeKeeper implements
NormalizedTimeKeeper {
+ private class ElapsedNormalizedTimeKeeper {
private final long zeroTimeNanos;
private final long zeroTimeMillis;
- public DefaultNormalizedTimeKeeper() {
+ public ElapsedNormalizedTimeKeeper() {
zeroTimeNanos = System.nanoTime();
zeroTimeMillis = (long) convertToMilliseconds(zeroTimeNanos);
}
@@ -232,20 +399,17 @@
public long normalizedTimeNanos() {
return System.nanoTime() - zeroTimeNanos;
}
-
- public void resetTimeBase() {
- }
public long zeroTimeMillis() {
return zeroTimeMillis;
}
}
-
- /*
+
+ /**
* Time keeper which uses process cpu time. This can be greater than
wall
- * time, since it is cummulative over the multiple threads of a process.
+ * time, since it is cumulative over the multiple threads of a process.
*/
- private class ProcessNormalizedTimeKeeper implements
NormalizedTimeKeeper {
+ private class ProcessNormalizedTimeKeeper {
private final OperatingSystemMXBean osMXBean;
private final Method getProcessCpuTimeMethod;
private final long zeroTimeNanos;
@@ -256,7 +420,7 @@
osMXBean = ManagementFactory.getOperatingSystemMXBean();
/*
* Find this method by reflection, since it's part of the Sun
- * implementation for OperatingSystemMXBean, and we can't alwayws
assume
+ * implementation for OperatingSystemMXBean, and we can't always
assume
* that com.sun.management.OperatingSystemMXBean will be available.
*/
getProcessCpuTimeMethod =
@@ -276,16 +440,13 @@
throw new RuntimeException(ex);
}
}
-
- public void resetTimeBase() {
- }
-
+
public long zeroTimeMillis() {
return zeroTimeMillis;
}
}
- /*
+ /**
* Time keeper which uses per thread cpu time. It is assumed that
individual
* events logged will be single threaded, and that top-level events will
call
* {@link #resetTimeBase()} prior to logging time. The resettable time
base
@@ -295,7 +456,7 @@
* output, although the relation to wall time is actually compressed
within
* a logged event (thread cpu time does not include wait time, etc.).
*/
- private class ThreadNormalizedTimeKeeper implements NormalizedTimeKeeper
{
+ private class ThreadNormalizedTimeKeeper {
private final ThreadMXBean threadMXBean;
private final ThreadLocal<Long> resettableTimeBase = new
ThreadLocal<Long>();
@@ -409,7 +570,7 @@
JsonObject toJson() {
JsonObject json = JsonObject.create();
json.put("type", 11);
- double startMs = convertToMilliseconds(startTimeNanos);
+ double startMs = convertToMilliseconds(getStartTimeNanos());
json.put("time", startMs);
json.put("duration", 0.0);
JsonObject jsonData = JsonObject.create();
@@ -457,18 +618,39 @@
public static void markTimeline(String message) {
SpeedTracerLogger.get().markTimelineImpl(message);
}
+
+ /**
+ * Signals that a new event has started. You must end each event for each
+ * corresponding call to {@code start}. You may nest timing calls.
+ *
+ * <p>
+ * Has the same effect as calling
+ * {@link #start(DevModeSession, EventType, String...)
+ * start(DevModeSession.getSessionForCurrentThread(), type, data)}.
+ *
+ * @param type the type of event
+ * @param data a set of key-value pairs (each key is followed by its
value)
+ * that contain additional information about the event
+ * @return an Event object to be ended by the caller
+ */
+ public static Event start(EventType type, String... data) {
+ return
SpeedTracerLogger.get().startImpl(DevModeSession.getSessionForCurrentThread(),
type,
+ data);
+ }
/**
* Signals that a new event has started. You must end each event for each
* corresponding call to {@code start}. You may nest timing calls.
*
+ * @param session the devmode session with which this event is
associated or
+ * null if no devmode session is active
* @param type the type of event
- * @data a set of key-value pairs (each key is followed by its value)
that
+ * @param data a set of key-value pairs (each key is followed by its
value) that
* contain additional information about the event
- * @return an Event object to be closed by the caller
+ * @return an Event object to be ended by the caller
*/
- public static Event start(EventType type, String... data) {
- return SpeedTracerLogger.get().startImpl(type, data);
+ public static Event start(DevModeSession session, EventType type,
String... data) {
+ return SpeedTracerLogger.get().startImpl(session, type, data);
}
private static double convertToMilliseconds(long nanos) {
@@ -496,91 +678,81 @@
private final boolean enabled;
- private final DummyEvent dummyEvent;
-
- private final BlockingQueue<Event> eventsToWrite;
-
+ private final DummyEvent dummyEvent = new DummyEvent();
+
+ private BlockingQueue<Event> eventsToWrite;
+
+ private final boolean fileLoggingEnabled;
+
private CountDownLatch flushLatch;
- private final Event flushSentinel;
-
- private final Format outputFormat;
-
- private final ThreadLocal<Stack<Event>> pendingEvents;
-
- private final CountDownLatch shutDownLatch;
-
- private final Event shutDownSentinel;
-
- private final List<GarbageCollectorMXBean> gcMXBeans;
-
- private final Map<String, Long> lastGcTimes;
-
- private final NormalizedTimeKeeper timeKeeper;
+ private Event flushSentinel;
+
+ private Format outputFormat;
+
+ private ThreadLocal<Stack<Event>> pendingEvents;
+
+ private CountDownLatch shutDownLatch;
+
+ private Event shutDownSentinel;
+
+ private List<GarbageCollectorMXBean> gcMXBeans;
+
+ private Map<String, Long> lastGcTimes;
+
+ private final ElapsedNormalizedTimeKeeper elapsedTimeKeeper = new
ElapsedNormalizedTimeKeeper();
+
+ private final ProcessNormalizedTimeKeeper processCpuTimeKeeper =
+ new ProcessNormalizedTimeKeeper();
+
+ private final ThreadNormalizedTimeKeeper threadCpuTimeKeeper = new
ThreadNormalizedTimeKeeper();
/**
* Constructor intended for unit testing.
- *
+ *
* @param writer alternative {@link Writer} to send speed tracer output.
*/
SpeedTracerLogger(Writer writer, Format format) {
enabled = true;
+ fileLoggingEnabled = true;
outputFormat = format;
eventsToWrite = openLogWriter(writer, "");
pendingEvents = initPendingEvents();
- timeKeeper = initTimeKeeper();
- gcMXBeans = null;
- lastGcTimes = null;
shutDownSentinel = new DummyEvent();
flushSentinel = new DummyEvent();
shutDownLatch = new CountDownLatch(1);
- dummyEvent = null;
}
private SpeedTracerLogger() {
- // Enabled flag (will be true if logFile is non-null)
- this.enabled = logFile != null;
-
+ fileLoggingEnabled = logFile != null;
+ enabled = fileLoggingEnabled ||
DashboardNotifierFactory.areNotificationsEnabled();
+
if (enabled) {
- // Allow a system property to override the default output format
- Format format = Format.HTML;
- if (defaultFormatString != null) {
- for (Format value : Format.values()) {
- if
(value.name().toLowerCase().equals(defaultFormatString.toLowerCase())) {
- format = value;
- break;
+ if (fileLoggingEnabled) {
+ // Allow a system property to override the default output format
+ Format format = Format.HTML;
+ if (defaultFormatString != null) {
+ for (Format value : Format.values()) {
+ if
(value.name().toLowerCase().equals(defaultFormatString.toLowerCase())) {
+ format = value;
+ break;
+ }
}
}
- }
-
- outputFormat = format;
- eventsToWrite = openDefaultLogWriter();
- pendingEvents = initPendingEvents();
- timeKeeper = initTimeKeeper();
-
+ outputFormat = format;
+ eventsToWrite = openDefaultLogWriter();
+
+ shutDownSentinel = new DummyEvent();
+ flushSentinel = new DummyEvent();
+ shutDownLatch = new CountDownLatch(1);
+ }
+
if (logGcTime) {
gcMXBeans = ManagementFactory.getGarbageCollectorMXBeans();
lastGcTimes = new ConcurrentHashMap<String, Long>();
- } else {
- gcMXBeans = null;
- lastGcTimes = null;
- }
-
- shutDownSentinel = new DummyEvent();
- flushSentinel = new DummyEvent();
- shutDownLatch = new CountDownLatch(1);
- dummyEvent = null;
- } else {
- outputFormat = null;
- eventsToWrite = null;
- pendingEvents = null;
- timeKeeper = null;
- gcMXBeans = null;
- lastGcTimes = null;
- shutDownSentinel = null;
- flushSentinel = null;
- shutDownLatch = null;
- dummyEvent = new DummyEvent();
+ }
+
+ pendingEvents = initPendingEvents();
}
}
@@ -607,7 +779,13 @@
}
void addGcEvents(Event refEvent) {
- for (java.lang.management.GarbageCollectorMXBean gcMXBean : gcMXBeans)
{
+ // we're not sending GC events to the dashboard, so we only record them
+ // to file
+ if (!fileLoggingEnabled) {
+ return;
+ }
+
+ for (GarbageCollectorMXBean gcMXBean : gcMXBeans) {
String gcName = gcMXBean.getName();
Long lastGcTime = lastGcTimes.get(gcName);
long currGcTime = gcMXBean.getCollectionTime();
@@ -617,26 +795,23 @@
if (currGcTime > lastGcTime) {
// create a new event
long gcDurationNanos = (currGcTime - lastGcTime) * 1000000L;
- long gcStartTimeNanos = refEvent.startTimeNanos +
refEvent.durationNanos
- - gcDurationNanos;
- Event gcEvent = new Event(gcStartTimeNanos, null,
- SpeedTracerEventType.GC, "Collector Type", gcName,
- "Cummulative Collection Count",
Long.toString(gcMXBean.getCollectionCount()));
- gcEvent.durationNanos = gcDurationNanos;
+ Event gcEvent =
+ new GcEvent(refEvent, gcName, gcMXBean.getCollectionCount(),
gcDurationNanos);
+
eventsToWrite.add(gcEvent);
-
lastGcTimes.put(gcName, currGcTime);
}
}
}
void addOverheadEvent(Event refEvent) {
- long overheadStartTime = refEvent.startTimeNanos +
refEvent.durationNanos;
- Event overheadEvent =
- new Event(overheadStartTime, refEvent,
SpeedTracerEventType.OVERHEAD);
- overheadEvent.durationNanos =
- timeKeeper.normalizedTimeNanos() - overheadStartTime;
- refEvent.durationNanos += overheadEvent.durationNanos;
+ Event overheadEvent =
+ new Event(refEvent.devModeSession, refEvent,
SpeedTracerEventType.OVERHEAD);
+ // measure the time between the end of refEvent and now
+ overheadEvent.setStartsAfter(refEvent);
+ overheadEvent.updateDuration();
+
+ refEvent.extendDuration(overheadEvent);
}
void endImpl(Event event, String... data) {
@@ -644,8 +819,6 @@
return;
}
- long endTimeNanos = timeKeeper.normalizedTimeNanos();
-
if (data.length % 2 == 1) {
throw new IllegalArgumentException("Unmatched data argument");
}
@@ -656,17 +829,14 @@
"Tried to end an event that never started!");
}
Event currentEvent = threadPendingEvents.pop();
-
- assert (endTimeNanos >= currentEvent.startTimeNanos);
- currentEvent.durationNanos = endTimeNanos -
currentEvent.startTimeNanos;
-
+ currentEvent.updateDuration();
+
while (currentEvent != event && !threadPendingEvents.isEmpty()) {
// Missed a closing end for one or more frames! Try to sync back up.
currentEvent.addData("Missed",
"This event was closed without an explicit call to Event.end()");
currentEvent = threadPendingEvents.pop();
- assert (endTimeNanos >= currentEvent.startTimeNanos);
- currentEvent.durationNanos = endTimeNanos -
currentEvent.startTimeNanos;
+ currentEvent.updateDuration();
}
if (threadPendingEvents.isEmpty() && currentEvent != event) {
@@ -685,7 +855,12 @@
}
if (threadPendingEvents.isEmpty()) {
- eventsToWrite.add(currentEvent);
+ if (fileLoggingEnabled) {
+ eventsToWrite.add(currentEvent);
+ }
+
+
DashboardNotifierFactory.getNotifier().devModeEvent(currentEvent.getDevModeSession(),
+ currentEvent);
}
}
@@ -695,6 +870,10 @@
* thread.
*/
void flush() {
+ if (!fileLoggingEnabled) {
+ return;
+ }
+
try {
// Wait for the other thread to drain the queue.
flushLatch = new CountDownLatch(1);
@@ -705,7 +884,7 @@
}
}
- Event startImpl(EventType type, String... data) {
+ Event startImpl(DevModeSession session, EventType type, String... data) {
if (!enabled) {
return dummyEvent;
}
@@ -719,15 +898,19 @@
if (!threadPendingEvents.isEmpty()) {
parent = threadPendingEvents.peek();
} else {
- // start new time base for top-level events
- timeKeeper.resetTimeBase();
+ // reset the thread CPU time base for top-level events (so events
can be
+ // properly sequenced chronologically)
+ threadCpuTimeKeeper.resetTimeBase();
}
- Event newEvent = new Event(parent, type, data);
+ Event newEvent = new Event(session, parent, type, data);
// Add a field to the top level event in order to track the base time
// so we can re-normalize the data
if (threadPendingEvents.size() == 0) {
- newEvent.addData("baseTime", "" + timeKeeper.zeroTimeMillis());
+ long baseTime = logProcessCpuTime ?
processCpuTimeKeeper.zeroTimeMillis()
+ : (logThreadCpuTime ? threadCpuTimeKeeper.zeroTimeMillis()
+ : elapsedTimeKeeper.zeroTimeMillis());
+ newEvent.addData("baseTime", "" + baseTime);
}
threadPendingEvents.push(newEvent);
return newEvent;
@@ -741,16 +924,6 @@
}
};
}
-
- private NormalizedTimeKeeper initTimeKeeper() {
- if (logProcessCpuTime) {
- return new ProcessNormalizedTimeKeeper();
- } else if (logThreadCpuTime) {
- return new ThreadNormalizedTimeKeeper();
- } else {
- return new DefaultNormalizedTimeKeeper();
- }
- }
private BlockingQueue<Event> openDefaultLogWriter() {
Writer writer = null;
=======================================
---
/trunk/dev/core/test/com/google/gwt/dev/shell/BrowserChannelServerTest.java
Fri Feb 19 06:35:15 2010
+++
/trunk/dev/core/test/com/google/gwt/dev/shell/BrowserChannelServerTest.java
Fri May 6 10:22:03 2011
@@ -208,6 +208,10 @@
assertNull(handler.getSessionKey());
assertNull(handler.getUserAgentIcon());
assertEquals(MessageType.RETURN, type);
+ DevModeSession session = server.getDevModeSession();
+ assertNotNull(session);
+ assertEquals("testModule", session.getModuleName());
+ assertEquals("userAgent", session.getUserAgent());
ReturnMessage.receive(client);
QuitMessage.send(client);
server.waitForClose();
@@ -246,6 +250,10 @@
assertEquals("session", handler.getSessionKey());
assertNull(handler.getUserAgentIcon());
assertEquals(MessageType.RETURN, type);
+ DevModeSession session = server.getDevModeSession();
+ assertNotNull(session);
+ assertEquals("testModule", session.getModuleName());
+ assertEquals("userAgent", session.getUserAgent());
ReturnMessage.receive(client);
QuitMessage.send(client);
server.waitForClose();
@@ -296,6 +304,10 @@
assertEquals(iconBytes[i], receivedIcon[i]);
}
assertEquals(MessageType.RETURN, type);
+ DevModeSession session = server.getDevModeSession();
+ assertNotNull(session);
+ assertEquals("testModule", session.getModuleName());
+ assertEquals("userAgent", session.getUserAgent());
ReturnMessage.receive(client);
QuitMessage.send(client);
server.waitForClose();
=======================================
---
/trunk/dev/core/test/com/google/gwt/dev/util/log/speedtracer/SpeedTracerLoggerTest.java
Wed Sep 15 10:32:35 2010
+++
/trunk/dev/core/test/com/google/gwt/dev/util/log/speedtracer/SpeedTracerLoggerTest.java
Fri May 6 10:22:03 2011
@@ -1,5 +1,5 @@
/*
- * Copyright 2010 Google Inc.
+ * Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
not
* use this file except in compliance with the License. You may obtain a
copy of
@@ -18,6 +18,9 @@
import com.google.gwt.dev.json.JsonArray;
import com.google.gwt.dev.json.JsonException;
import com.google.gwt.dev.json.JsonObject;
+import com.google.gwt.dev.shell.DevModeSession;
+import com.google.gwt.dev.shell.DevModeSessionTestUtil;
+import
com.google.gwt.dev.util.log.dashboard.SpeedTracerLoggerTestMockNotifier;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.EventType;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Format;
@@ -30,6 +33,8 @@
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
+import java.util.LinkedList;
+import java.util.Properties;
/**
* Tests the SpeedTracerLogger class.
@@ -44,10 +49,12 @@
this.color = color;
}
+ @Override
public String getColor() {
return color;
}
+ @Override
public String getName() {
return name;
}
@@ -66,7 +73,7 @@
@Override
public void run() {
for (int i = 0; i < MAX_EVENT_LOGS; i++) {
- Event e = logger.startImpl(event);
+ Event e = logger.startImpl(null, event);
logger.endImpl(e);
}
}
@@ -83,10 +90,10 @@
public void testSpeedTracerLogger() throws IOException, JsonException {
Writer writer = new StringWriter();
SpeedTracerLogger logger = new SpeedTracerLogger(writer, Format.HTML);
- Event dummyOneEvent = logger.startImpl(dummyOne);
- Event dummyTwoEvent = logger.startImpl(dummyTwo);
+ Event dummyOneEvent = logger.startImpl(null, dummyOne);
+ Event dummyTwoEvent = logger.startImpl(null, dummyTwo);
logger.endImpl(dummyTwoEvent);
- Event dummyThreeEvent = logger.startImpl(dummyThree);
+ Event dummyThreeEvent = logger.startImpl(null, dummyThree);
logger.endImpl(dummyThreeEvent);
logger.endImpl(dummyOneEvent);
logger.flush();
@@ -113,7 +120,7 @@
JsonException {
Writer writer = new StringWriter();
SpeedTracerLogger logger = new SpeedTracerLogger(writer, Format.HTML);
- Event dummyOneEvent =
logger.startImpl(dummyOne, "extraStart", "valueStart");
+ Event dummyOneEvent = logger.startImpl(null,
dummyOne, "extraStart", "valueStart");
logger.addDataImpl("extraMiddle", "valueMiddle");
logger.endImpl(dummyOneEvent, "extraEnd", "valueEnd");
logger.flush();
@@ -133,11 +140,11 @@
public void testSpeedTracerLoggerMultiple() throws IOException,
JsonException {
Writer writer = new StringWriter();
SpeedTracerLogger logger = new SpeedTracerLogger(writer, Format.HTML);
- Event dummyOneEvent = logger.startImpl(dummyOne);
+ Event dummyOneEvent = logger.startImpl(null, dummyOne);
logger.endImpl(dummyOneEvent);
- Event dummyTwoEvent = logger.startImpl(dummyTwo);
+ Event dummyTwoEvent = logger.startImpl(null, dummyTwo);
logger.endImpl(dummyTwoEvent);
- Event dummyThreeEvent = logger.startImpl(dummyThree);
+ Event dummyThreeEvent = logger.startImpl(null, dummyThree);
logger.endImpl(dummyThreeEvent);
logger.flush();
@@ -199,7 +206,7 @@
public void testSpeedTracerLoggerMarkTimeline() throws IOException,
JsonException {
Writer writer = new StringWriter();
SpeedTracerLogger logger = new SpeedTracerLogger(writer, Format.RAW);
- Event dummyOneEvent = logger.startImpl(dummyOne);
+ Event dummyOneEvent = logger.startImpl(null, dummyOne);
logger.markTimelineImpl("Test Message");
dummyOneEvent.end();
logger.flush();
@@ -222,7 +229,7 @@
public void testSpeedTracerLoggerRaw() throws IOException, JsonException
{
Writer writer = new StringWriter();
SpeedTracerLogger logger = new SpeedTracerLogger(writer, Format.RAW);
- Event dummyOneEvent = logger.startImpl(dummyOne);
+ Event dummyOneEvent = logger.startImpl(null, dummyOne);
dummyOneEvent.end();
logger.flush();
@@ -261,4 +268,74 @@
}
return jsonReader;
}
-}
+
+ public void testSpeedTracerWhenOnlyDashboardEnabled() {
+ // backup system properties before making changes to them
+ Properties props = (Properties) System.getProperties().clone();
+
+ try {
+ // no logging to file!
+ System.clearProperty("gwt.speedtracerlog");
+ // we don't capture GC events in dashboard, so setting this will
allow us
+ // to confirm that they *don't* show up in dashboard notices
+ System.setProperty("gwt.speedtracer.logGcTime", "yes");
+
+ // now enable the mock dashboard notifier
+ SpeedTracerLoggerTestMockNotifier notifier =
SpeedTracerLoggerTestMockNotifier.enable();
+
+ // create "sessions"
+ DevModeSession session1 =
DevModeSessionTestUtil.createSession("test1", "test", true);
+ DevModeSession session2 =
DevModeSessionTestUtil.createSession("test2", "test", false);
+
+ // expected values (used in final assertions below)
+ LinkedList<Event> expectedEvents = new LinkedList<Event>();
+ LinkedList<DevModeSession> expectedSessions = new
LinkedList<DevModeSession>();
+
+ Event evt1, evt2;
+
+ // test events with no session specified
+ evt1 =
SpeedTracerLogger.start(DevModeEventType.MODULE_INIT, "k1", "v1", "k2", "v2");
+ // also test that child events aren't posted (only top-level events)
+ evt2 = SpeedTracerLogger.start(DevModeEventType.CLASS_BYTES_REWRITE);
+ evt2.end();
+ evt1.end();
+ // expect only first event
+ expectedEvents.add(evt1);
+ expectedSessions.add(session1); // event should get "default" session
+
+ // now with session specified
+ evt1 = SpeedTracerLogger.start(session2,
DevModeEventType.JAVA_TO_JS_CALL, "k1", "v1");
+ // also test that child events aren't posted (only top-level events)
+ evt2 = SpeedTracerLogger.start(DevModeEventType.CREATE_UI);
+ evt2.end();
+ evt1.end();
+ // expect only first event
+ expectedEvents.add(evt1);
+ expectedSessions.add(session2);
+
+ evt1 = SpeedTracerLogger.start(session1,
DevModeEventType.JS_TO_JAVA_CALL, "k1", "v1");
+ evt1.end();
+ expectedEvents.add(evt1);
+ expectedSessions.add(session1);
+
+ // Finally, assert that the events and corresponding sessions sent
to the
+ // notifier are exactly as expected
+ assertEquals("Events posted to dashboard do not match expected
events!", expectedEvents,
+ notifier.getEventSequence());
+
+ // Collect sessions associated with each event
+ LinkedList<DevModeSession> actualSessions = new
LinkedList<DevModeSession>();
+ for (Event event : notifier.getEventSequence()) {
+ actualSessions.add(event.getDevModeSession());
+ }
+
+ // and confirm the sessions are correct
+ assertEquals("Events posted to dashboard are associated with
incorrect sessions!",
+ expectedSessions, actualSessions);
+
+ } finally {
+ // restore system properties
+ System.setProperties(props);
+ }
+ }
+}
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors