From: Leif Åstrand Date: Tue, 12 Feb 2013 10:20:29 +0000 (+0200) Subject: Add lightweight profiling (#10961) X-Git-Tag: 7.0.1~36^2 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=34096d1784c1d5e0624d016363d4191451c4f6e9;p=vaadin-framework.git Add lightweight profiling (#10961) Also remove most of the timing information that was previously logged Change-Id: I8269036a12762eb63f7d4f93aefb6be307dd620a --- diff --git a/client/src/com/vaadin/Vaadin.gwt.xml b/client/src/com/vaadin/Vaadin.gwt.xml index f7d1cf8410..dcc5b0d294 100644 --- a/client/src/com/vaadin/Vaadin.gwt.xml +++ b/client/src/com/vaadin/Vaadin.gwt.xml @@ -38,6 +38,15 @@ + + + + + + + + + diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java index 0171d541ea..44c7397655 100644 --- a/client/src/com/vaadin/client/ApplicationConnection.java +++ b/client/src/com/vaadin/client/ApplicationConnection.java @@ -1311,6 +1311,8 @@ public class ApplicationConnection { return; } + Profiler.reset(); + VConsole.log("Handling message from server"); eventBus.fireEvent(new ResponseHandlingStartedEvent(this)); @@ -1384,8 +1386,7 @@ public class ApplicationConnection { handleUIDLDuration.logDuration(" * Loading widgets completed", 10); - MultiStepDuration updateDuration = new MultiStepDuration(); - + Profiler.enter("Handling locales"); if (json.containsKey("locales")) { VConsole.log(" * Handling locales"); // Store locale data @@ -1393,9 +1394,9 @@ public class ApplicationConnection { .getJSValueMapArray("locales"); LocaleService.addLocales(valueMapArray); } + Profiler.leave("Handling locales"); - updateDuration.logDuration(" * Handling locales completed", 10); - + Profiler.enter("Handling meta information"); boolean repaintAll = false; ValueMap meta = null; if (json.containsKey("meta")) { @@ -1422,9 +1423,7 @@ public class ApplicationConnection { .getInt("interval"); } } - - updateDuration.logDuration( - " * Handling meta information completed", 10); + Profiler.leave("Handling meta information"); if (redirectTimer != null) { redirectTimer.schedule(1000 * sessionExpirationInterval); @@ -1432,34 +1431,21 @@ public class ApplicationConnection { componentCaptionSizeChanges.clear(); - int startProcessing = updateDuration.elapsedMillis(); + double processUidlStart = Duration.currentTimeMillis(); // Ensure that all connectors that we are about to update exist Set createdConnectors = createConnectorsIfNeeded(json); - updateDuration.logDuration(" * Creating connectors completed", - 10); - // Update states, do not fire events Collection pendingStateChangeEvents = updateConnectorState( json, createdConnectors); - updateDuration.logDuration( - " * Update of connector states completed", 10); - // Update hierarchy, do not fire events ConnectorHierarchyUpdateResult connectorHierarchyUpdateResult = updateConnectorHierarchy(json); - updateDuration.logDuration( - " * Update of connector hierarchy completed", 10); - // Fire hierarchy change events sendHierarchyChangeEvents(connectorHierarchyUpdateResult.events); - updateDuration.logDuration( - " * Hierarchy state change event processing completed", - 10); - updateCaptions(pendingStateChangeEvents, connectorHierarchyUpdateResult.parentChanged); @@ -1468,43 +1454,25 @@ public class ApplicationConnection { // Fire state change events. sendStateChangeEvents(pendingStateChangeEvents); - updateDuration.logDuration( - " * State change event processing completed", 10); - // Update of legacy (UIDL) style connectors updateVaadin6StyleConnectors(json); - updateDuration - .logDuration( - " * Vaadin 6 style connector updates (updateFromUidl) completed", - 10); - // Handle any RPC invocations done on the server side handleRpcInvocations(json); - updateDuration.logDuration( - " * Processing of RPC invocations completed", 10); - if (json.containsKey("dd")) { // response contains data for drag and drop service VDragAndDropManager.get().handleServerResponse( json.getValueMap("dd")); } - updateDuration - .logDuration( - " * Processing of drag and drop server response completed", - 10); - unregisterRemovedConnectors(); - updateDuration.logDuration( - " * Unregistering of removed components completed", 10); - VConsole.log("handleUIDLMessage: " - + (updateDuration.elapsedMillis() - startProcessing) + + (Duration.currentTimeMillis() - processUidlStart) + " ms"); + Profiler.enter("Layout processing"); try { LayoutManager layoutManager = getLayoutManager(); layoutManager.setEverythingNeedsMeasure(); @@ -1512,21 +1480,17 @@ public class ApplicationConnection { } catch (final Throwable e) { VConsole.error(e); } - - updateDuration - .logDuration(" * Layout processing completed", 10); + Profiler.leave("Layout processing"); if (ApplicationConfiguration.isDebugMode()) { + Profiler.enter("Dumping state changes to the console"); VConsole.log(" * Dumping state changes to the console"); VConsole.dirUIDL(json, ApplicationConnection.this); - - updateDuration - .logDuration( - " * Dumping state changes to the console completed", - 10); + Profiler.leave("Dumping state changes to the console"); } if (meta != null) { + Profiler.enter("Error handling"); if (meta.containsKey("appError")) { ValueMap error = meta.getValueMap("appError"); @@ -1547,10 +1511,9 @@ public class ApplicationConnection { validatingLayouts = false; } + Profiler.leave("Error handling"); } - updateDuration.logDuration(" * Error handling completed", 10); - // TODO build profiling for widget impl loading time lastProcessingTime = (int) ((new Date().getTime()) - start @@ -1564,11 +1527,18 @@ public class ApplicationConnection { endRequest(); + if (Profiler.isEnabled()) { + Profiler.logTimings(); + Profiler.reset(); + } + } private void updateCaptions( Collection pendingStateChangeEvents, Collection parentChanged) { + Profiler.enter("updateCaptions"); + /* * Find all components that might need a caption update based on * pending state and hierarchy changes @@ -1589,15 +1559,21 @@ public class ApplicationConnection { .delegateCaptionHandling()) { ServerConnector parent = child.getParent(); if (parent instanceof HasComponentsConnector) { + Profiler.enter("HasComponentsConnector.updateCaption"); ((HasComponentsConnector) parent) .updateCaption((ComponentConnector) child); + Profiler.leave("HasComponentsConnector.updateCaption"); } } } + + Profiler.leave("updateCaptions"); } private void delegateToWidget( Collection pendingStateChangeEvents) { + Profiler.enter("@DelegateToWidget"); + VConsole.log(" * Running @DelegateToWidget"); for (StateChangeEvent sce : pendingStateChangeEvents) { @@ -1618,12 +1594,16 @@ public class ApplicationConnection { String method = property .getDelegateToWidgetMethodName(); if (method != null) { + Profiler.enter("doDelegateToWidget"); doDelegateToWidget(component, property, method); + Profiler.leave("doDelegateToWidget"); } } } } + + Profiler.leave("@DelegateToWidget"); } private void doDelegateToWidget(ComponentConnector component, @@ -1661,6 +1641,7 @@ public class ApplicationConnection { */ private void sendStateChangeEvents( Collection pendingStateChangeEvents) { + Profiler.enter("sendStateChangeEvents"); VConsole.log(" * Sending state change events"); for (StateChangeEvent sce : pendingStateChangeEvents) { @@ -1671,9 +1652,12 @@ public class ApplicationConnection { } } + Profiler.leave("sendStateChangeEvents"); } private void unregisterRemovedConnectors() { + Profiler.enter("unregisterRemovedConnectors"); + int unregistered = 0; JsArrayObject currentConnectors = connectorMap .getConnectorsAsJsArray(); @@ -1703,6 +1687,7 @@ public class ApplicationConnection { } VConsole.log("* Unregistered " + unregistered + " connectors"); + Profiler.leave("unregisterRemovedConnectors"); } private Set createConnectorsIfNeeded(ValueMap json) { @@ -1712,6 +1697,8 @@ public class ApplicationConnection { return Collections.emptySet(); } + Profiler.enter("Creating connectors"); + Set createdConnectors = new HashSet(); ValueMap types = json.getValueMap("types"); @@ -1733,7 +1720,10 @@ public class ApplicationConnection { // Connector does not exist so we must create it if (connectorClass != UIConnector.class) { // create, initialize and register the paintable + Profiler.enter("ApplicationConnection.getConnector"); connector = getConnector(connectorId, connectorType); + Profiler.leave("ApplicationConnection.getConnector"); + createdConnectors.add(connector); } else { // First UIConnector update. Before this the @@ -1750,10 +1740,15 @@ public class ApplicationConnection { VConsole.error(e); } } + + Profiler.leave("Creating connectors"); + return createdConnectors; } private void updateVaadin6StyleConnectors(ValueMap json) { + Profiler.enter("updateVaadin6StyleConnectors"); + JsArray changes = json.getJSValueMapArray("changes"); int length = changes.length(); @@ -1768,8 +1763,19 @@ public class ApplicationConnection { final ComponentConnector legacyConnector = (ComponentConnector) connectorMap .getConnector(connectorId); if (legacyConnector instanceof Paintable) { + String key = null; + if (Profiler.isEnabled()) { + key = "updateFromUIDL for " + + Util.getSimpleName(legacyConnector); + Profiler.enter(key); + } + ((Paintable) legacyConnector).updateFromUIDL(uidl, ApplicationConnection.this); + + if (Profiler.isEnabled()) { + Profiler.leave(key); + } } else if (legacyConnector == null) { VConsole.error("Received update for " + uidl.getTag() @@ -1785,6 +1791,8 @@ public class ApplicationConnection { VConsole.error(e); } } + + Profiler.leave("updateVaadin6StyleConnectors"); } private void sendHierarchyChangeEvents( @@ -1792,6 +1800,7 @@ public class ApplicationConnection { if (pendingHierarchyChangeEvents.isEmpty()) { return; } + Profiler.enter("sendHierarchyChangeEvents"); VConsole.log(" * Sending hierarchy change events"); for (ConnectorHierarchyChangeEvent event : pendingHierarchyChangeEvents) { @@ -1803,6 +1812,7 @@ public class ApplicationConnection { } } + Profiler.leave("sendHierarchyChangeEvents"); } private void logHierarchyChange(ConnectorHierarchyChangeEvent event) { @@ -1835,6 +1845,9 @@ public class ApplicationConnection { if (!json.containsKey("state")) { return events; } + + Profiler.enter("updateConnectorState"); + HashSet remainingNewConnectors = new HashSet( newConnectors); @@ -1847,6 +1860,11 @@ public class ApplicationConnection { ServerConnector connector = connectorMap .getConnector(connectorId); if (null != connector) { + Profiler.enter("updateConnectorState inner loop"); + if (Profiler.isEnabled()) { + Profiler.enter("Decode connector state " + + Util.getSimpleName(connector)); + } JSONObject stateJson = new JSONObject( states.getJavaScriptObject(connectorId)); @@ -1859,10 +1877,19 @@ public class ApplicationConnection { } SharedState state = connector.getState(); + + Profiler.enter("updateConnectorState decodeValue"); JsonDecoder.decodeValue(new Type(state.getClass() .getName(), null), stateJson, state, ApplicationConnection.this); + Profiler.leave("updateConnectorState decodeValue"); + + if (Profiler.isEnabled()) { + Profiler.leave("Decode connector state " + + Util.getSimpleName(connector)); + } + Profiler.enter("updateConnectorState create event"); FastStringSet changedProperties = FastStringSet .create(); addJsonFields(stateJson, changedProperties, ""); @@ -1880,6 +1907,9 @@ public class ApplicationConnection { connector, changedProperties); events.add(event); + Profiler.leave("updateConnectorState create event"); + + Profiler.leave("updateConnectorState inner loop"); } } catch (final Throwable e) { VConsole.error(e); @@ -1899,6 +1929,8 @@ public class ApplicationConnection { } + Profiler.leave("updateConnectorState"); + return events; } @@ -1906,9 +1938,11 @@ public class ApplicationConnection { FastStringSet fields; fields = allStateFieldsCache.get(type.getBaseTypeName()); if (fields == null) { + Profiler.enter("getAllStateFields create"); fields = FastStringSet.create(); addAllStateFields(type, fields, ""); allStateFieldsCache.put(type.getBaseTypeName(), fields); + Profiler.leave("getAllStateFields create"); } return fields; } @@ -1994,6 +2028,8 @@ public class ApplicationConnection { return result; } + Profiler.enter("updateConnectorHierarchy"); + FastStringSet maybeDetached = FastStringSet.create(); ValueMap hierarchies = json.getValueMap("hierarchy"); @@ -2113,6 +2149,8 @@ public class ApplicationConnection { recursivelyDetach(removed, result.events); } + Profiler.leave("updateConnectorHierarchy"); + return result; } @@ -2200,6 +2238,8 @@ public class ApplicationConnection { private void handleRpcInvocations(ValueMap json) { if (json.containsKey("rpc")) { + Profiler.enter("handleRpcInvocations"); + VConsole.log(" * Performing server to client RPC calls"); JSONArray rpcCalls = new JSONArray( @@ -2215,8 +2255,9 @@ public class ApplicationConnection { VConsole.error(e); } } - } + Profiler.leave("handleRpcInvocations"); + } } }; @@ -2864,12 +2905,15 @@ public class ApplicationConnection { */ private ServerConnector createAndRegisterConnector(String connectorId, int connectorType) { + Profiler.enter("ApplicationConnection.createAndRegisterConnector"); + // Create and register a new connector with the given type ServerConnector p = widgetSet.createConnector(connectorType, configuration); connectorMap.registerConnector(connectorId, p); p.doInit(connectorId, this); + Profiler.leave("ApplicationConnection.createAndRegisterConnector"); return p; } diff --git a/client/src/com/vaadin/client/ConnectorMap.java b/client/src/com/vaadin/client/ConnectorMap.java index 50df65397f..5f6053dd32 100644 --- a/client/src/com/vaadin/client/ConnectorMap.java +++ b/client/src/com/vaadin/client/ConnectorMap.java @@ -120,13 +120,17 @@ public class ConnectorMap { } public void registerConnector(String id, ServerConnector connector) { + Profiler.enter("ConnectorMap.registerConnector"); ComponentDetail componentDetail = GWT.create(ComponentDetail.class); idToComponentDetail.put(id, componentDetail); componentDetail.setConnector(connector); if (connector instanceof ComponentConnector) { ComponentConnector pw = (ComponentConnector) connector; + Profiler.enter("ConnectorMap.setConnectorId"); setConnectorId(pw.getWidget().getElement(), id); + Profiler.leave("ConnectorMap.setConnectorId"); } + Profiler.leave("ConnectorMap.registerConnector"); } private static native void setConnectorId(Element el, String id) diff --git a/client/src/com/vaadin/client/LayoutManager.java b/client/src/com/vaadin/client/LayoutManager.java index d3366e86f3..14b155c92f 100644 --- a/client/src/com/vaadin/client/LayoutManager.java +++ b/client/src/com/vaadin/client/LayoutManager.java @@ -259,6 +259,7 @@ public class LayoutManager { private void doLayout() { VConsole.log("Starting layout phase"); + Profiler.enter("LayoutManager phase init"); FastStringMap layoutCounts = FastStringMap.create(); @@ -293,27 +294,30 @@ public class LayoutManager { measureNonConnectors(); - VConsole.log("Layout init in " + totalDuration.elapsedMillis() + " ms"); + Profiler.leave("LayoutManager phase init"); while (true) { - Duration passDuration = new Duration(); + Profiler.enter("Layout pass"); passes++; performBrowserLayoutHacks(); + Profiler.enter("Layout measure connectors"); int measuredConnectorCount = measureConnectors( currentDependencyTree, everythingNeedsMeasure); + Profiler.leave("Layout measure connectors"); + everythingNeedsMeasure = false; if (measuredConnectorCount == 0) { VConsole.log("No more changes in pass " + passes); + Profiler.leave("Layout pass"); break; } - int measureTime = passDuration.elapsedMillis(); - VConsole.log(" Measured " + measuredConnectorCount - + " elements in " + measureTime + " ms"); - + int firedListeners = 0; if (!listenersToFire.isEmpty()) { + firedListeners = listenersToFire.size(); + Profiler.enter("Layout fire resize events"); for (Element element : listenersToFire) { Collection listeners = elementResizeListeners .get(element); @@ -325,23 +329,33 @@ public class LayoutManager { element); for (ElementResizeListener listener : array) { try { + String key = null; + if (Profiler.isEnabled()) { + key = "ElementReizeListener.onElementReize for " + + Util.getSimpleName(listener); + Profiler.enter(key); + } + listener.onElementResize(event); + if (Profiler.isEnabled()) { + Profiler.leave(key); + } } catch (RuntimeException e) { VConsole.error(e); } } } } - int measureListenerTime = passDuration.elapsedMillis(); - VConsole.log(" Fired resize listeners for " - + listenersToFire.size() + " elements in " - + (measureListenerTime - measureTime) + " ms"); - measureTime = measuredConnectorCount; listenersToFire.clear(); + + Profiler.leave("Layout fire resize events"); } + Profiler.enter("LayoutManager handle ManagedLayout"); + FastStringSet updatedSet = FastStringSet.create(); + int layoutCount = 0; while (currentDependencyTree.hasHorizontalConnectorToLayout() || currentDependencyTree.hasVerticaConnectorToLayout()) { @@ -356,7 +370,19 @@ public class LayoutManager { .markAsHorizontallyLayouted(layout); DirectionalManagedLayout cl = (DirectionalManagedLayout) layout; try { + String key = null; + if (Profiler.isEnabled()) { + key = "layoutHorizontally() for " + + Util.getSimpleName(cl); + Profiler.enter(key); + } + cl.layoutHorizontally(); + layoutCount++; + + if (Profiler.isEnabled()) { + Profiler.leave(key); + } } catch (RuntimeException e) { VConsole.error(e); } @@ -367,7 +393,18 @@ public class LayoutManager { currentDependencyTree.markAsVerticallyLayouted(layout); SimpleManagedLayout rr = (SimpleManagedLayout) layout; try { + String key = null; + if (Profiler.isEnabled()) { + key = "layout() for " + Util.getSimpleName(rr); + Profiler.enter(key); + } + rr.layout(); + layoutCount++; + + if (Profiler.isEnabled()) { + Profiler.leave(key); + } } catch (RuntimeException e) { VConsole.error(e); } @@ -388,7 +425,19 @@ public class LayoutManager { currentDependencyTree.markAsVerticallyLayouted(layout); DirectionalManagedLayout cl = (DirectionalManagedLayout) layout; try { + String key = null; + if (Profiler.isEnabled()) { + key = "layoutHorizontally() for " + + Util.getSimpleName(cl); + Profiler.enter(key); + } + cl.layoutVertically(); + layoutCount++; + + if (Profiler.isEnabled()) { + Profiler.leave(key); + } } catch (RuntimeException e) { VConsole.error(e); } @@ -399,7 +448,18 @@ public class LayoutManager { currentDependencyTree.markAsVerticallyLayouted(layout); SimpleManagedLayout rr = (SimpleManagedLayout) layout; try { + String key = null; + if (Profiler.isEnabled()) { + key = "layout() for " + Util.getSimpleName(rr); + Profiler.enter(key); + } + rr.layout(); + layoutCount++; + + if (Profiler.isEnabled()) { + Profiler.leave(key); + } } catch (RuntimeException e) { VConsole.error(e); } @@ -411,14 +471,14 @@ public class LayoutManager { } } + Profiler.leave("LayoutManager handle ManagedLayout"); + if (debugLogging) { JsArrayString changedCids = updatedSet.dump(); StringBuilder b = new StringBuilder(" "); b.append(changedCids.length()); - b.append(" requestLayout invocations in "); - b.append(passDuration.elapsedMillis() - measureTime); - b.append(" ms"); + b.append(" requestLayout invocations "); if (changedCids.length() < 30) { for (int i = 0; i < changedCids.length(); i++) { if (i != 0) { @@ -439,8 +499,12 @@ public class LayoutManager { VConsole.log(b.toString()); } - VConsole.log("Pass " + passes + " completed in " - + passDuration.elapsedMillis() + " ms"); + Profiler.leave("Layout pass"); + + VConsole.log("Pass " + passes + " measured " + + measuredConnectorCount + " elements, fired " + + firedListeners + " listeners and did " + layoutCount + + " layouts."); if (passes > 100) { VConsole.log(LOOP_ABORT_MESSAGE); @@ -455,23 +519,30 @@ public class LayoutManager { } } - int postLayoutStart = totalDuration.elapsedMillis(); + Profiler.enter("layout PostLayoutListener"); JsArrayObject componentConnectors = connectorMap .getComponentConnectorsAsJsArray(); int size = componentConnectors.size(); for (int i = 0; i < size; i++) { ComponentConnector connector = componentConnectors.get(i); if (connector instanceof PostLayoutListener) { + String key = null; + if (Profiler.isEnabled()) { + key = "layout PostLayoutListener for " + + Util.getSimpleName(connector); + Profiler.enter(key); + } + ((PostLayoutListener) connector).postLayout(); + + if (Profiler.isEnabled()) { + Profiler.leave(key); + } } } - int postLayoutDone = totalDuration.elapsedMillis(); - VConsole.log("Invoke post layout listeners in " - + (postLayoutDone - postLayoutStart) + " ms"); + Profiler.leave("layout PostLayoutListener"); cleanMeasuredSizes(); - int cleaningTime = (totalDuration.elapsedMillis() - postLayoutDone); - VConsole.log("Cleaned old measured sizes in " + cleaningTime + "ms"); VConsole.log("Total layout phase time: " + totalDuration.elapsedMillis() + "ms"); @@ -485,13 +556,12 @@ public class LayoutManager { private int measureConnectors(LayoutDependencyTree layoutDependencyTree, boolean measureAll) { + Profiler.enter("Layout overflow fix handling"); JsArrayString pendingOverflowConnectorsIds = pendingOverflowFixes .dump(); int pendingOverflowCount = pendingOverflowConnectorsIds.length(); ConnectorMap connectorMap = ConnectorMap.get(connection); if (pendingOverflowCount > 0) { - Duration duration = new Duration(); - HashMap originalOverflows = new HashMap(); FastStringSet delayedOverflowFixes = FastStringSet.create(); @@ -523,6 +593,7 @@ public class LayoutManager { + Util.getConnectorString(componentConnector .getParent())); } + Profiler.enter("Overflow fix apply"); Element parentElement = componentConnector.getWidget() .getElement().getParentElement(); @@ -537,6 +608,7 @@ public class LayoutManager { } style.setOverflow(Overflow.HIDDEN); + Profiler.leave("Overflow fix apply"); } pendingOverflowFixes.removeAll(delayedOverflowFixes); @@ -544,6 +616,7 @@ public class LayoutManager { JsArrayString remainingOverflowFixIds = pendingOverflowFixes.dump(); int remainingCount = remainingOverflowFixIds.length(); + Profiler.enter("Overflow fix reflow"); // Then ensure all scrolling elements are reflowed by measuring for (int i = 0; i < remainingCount; i++) { ComponentConnector componentConnector = (ComponentConnector) connectorMap @@ -551,7 +624,9 @@ public class LayoutManager { componentConnector.getWidget().getElement().getParentElement() .getOffsetHeight(); } + Profiler.leave("Overflow fix reflow"); + Profiler.enter("Overflow fix restore"); // Finally restore old overflow value and update bookkeeping for (int i = 0; i < remainingCount; i++) { String connectorId = remainingOverflowFixIds.get(i); @@ -564,15 +639,19 @@ public class LayoutManager { layoutDependencyTree.setNeedsMeasure(connectorId, true); } + Profiler.leave("Overflow fix restore"); + if (!pendingOverflowFixes.isEmpty()) { VConsole.log("Did overflow fix for " + remainingCount - + " elements in " + duration.elapsedMillis() + " ms"); + + " elements"); } pendingOverflowFixes = delayedOverflowFixes; } + Profiler.leave("Layout overflow fix handling"); int measureCount = 0; if (measureAll) { + Profiler.enter("Layout measureAll"); JsArrayObject allConnectors = connectorMap .getComponentConnectorsAsJsArray(); int size = allConnectors.size(); @@ -596,8 +675,11 @@ public class LayoutManager { .getConnectorId(), false); } measureCount += connectorCount; + + Profiler.leave("Layout measureAll"); } + Profiler.enter("Layout measure from tree"); while (layoutDependencyTree.hasConnectorsToMeasure()) { JsArrayString measureTargets = layoutDependencyTree .getMeasureTargetsJsArray(); @@ -613,10 +695,13 @@ public class LayoutManager { layoutDependencyTree.setNeedsMeasure(connectorId, false); } } + Profiler.leave("Layout measure from tree"); + return measureCount; } private void measureConnector(ComponentConnector connector) { + Profiler.enter("LayoutManager.measureConnector"); Element element = connector.getWidget().getElement(); MeasuredSize measuredSize = getMeasuredSize(connector); MeasureResult measureResult = measuredAndUpdate(element, measuredSize); @@ -625,10 +710,12 @@ public class LayoutManager { onConnectorChange(connector, measureResult.isWidthChanged(), measureResult.isHeightChanged()); } + Profiler.leave("LayoutManager.measureConnector"); } private void onConnectorChange(ComponentConnector connector, boolean widthChanged, boolean heightChanged) { + Profiler.enter("LayoutManager.onConnectorChange"); setNeedsOverflowFix(connector); if (heightChanged) { currentDependencyTree.markHeightAsChanged(connector); @@ -636,6 +723,7 @@ public class LayoutManager { if (widthChanged) { currentDependencyTree.markWidthAsChanged(connector); } + Profiler.leave("LayoutManager.onConnectorChange"); } private void setNeedsOverflowFix(ComponentConnector connector) { @@ -651,9 +739,11 @@ public class LayoutManager { } private void measureNonConnectors() { + Profiler.enter("LayoutManager.measureNonConenctors"); for (Element element : measuredNonConnectorElements) { measuredAndUpdate(element, getMeasuredSize(element, null)); } + Profiler.leave("LayoutManager.measureNonConenctors"); VConsole.log("Measured " + measuredNonConnectorElements.size() + " non connector elements"); } @@ -673,6 +763,8 @@ public class LayoutManager { boolean widthChanged, boolean heightChanged) { assert widthChanged || heightChanged; + Profiler.enter("LayoutManager.notifyListenersAndDepdendents"); + MeasuredSize measuredSize = getMeasuredSize(element, nullSize); JsArrayString dependents = measuredSize.getDependents(); for (int i = 0; i < dependents.length(); i++) { @@ -689,6 +781,7 @@ public class LayoutManager { if (elementResizeListeners.containsKey(element)) { listenersToFire.add(element); } + Profiler.leave("LayoutManager.notifyListenersAndDepdendents"); } private static boolean isManagedLayout(ComponentConnector connector) { diff --git a/client/src/com/vaadin/client/LayoutManagerIE8.java b/client/src/com/vaadin/client/LayoutManagerIE8.java index b352e14dc6..a692f126a2 100644 --- a/client/src/com/vaadin/client/LayoutManagerIE8.java +++ b/client/src/com/vaadin/client/LayoutManagerIE8.java @@ -61,6 +61,7 @@ public class LayoutManagerIE8 extends LayoutManager { @Override protected void cleanMeasuredSizes() { + Profiler.enter("LayoutManager.cleanMeasuredSizes"); Document document = RootPanel.get().getElement().getOwnerDocument(); Iterator i = measuredSizes.keySet().iterator(); @@ -70,15 +71,19 @@ public class LayoutManagerIE8 extends LayoutManager { i.remove(); } } + + Profiler.leave("LayoutManager.cleanMeasuredSizes"); } @Override protected void performBrowserLayoutHacks() { + Profiler.enter("LayoutManagerIE8.performBrowserLayoutHacks"); /* * Fixes IE8 issues where IE8 sometimes forgets to update the size of * the containing element. To force a reflow by modifying the magical * zoom property. */ Util.forceIE8Redraw(RootPanel.get().getElement()); + Profiler.leave("LayoutManagerIE8.performBrowserLayoutHacks"); } } diff --git a/client/src/com/vaadin/client/MeasuredSize.java b/client/src/com/vaadin/client/MeasuredSize.java index e0928a1702..51da8570e6 100644 --- a/client/src/com/vaadin/client/MeasuredSize.java +++ b/client/src/com/vaadin/client/MeasuredSize.java @@ -185,11 +185,18 @@ public class MeasuredSize { } public MeasureResult measure(Element element) { + Profiler.enter("MeasuredSize.measure"); boolean heightChanged = false; boolean widthChanged = false; + Profiler.enter("new ComputedStyle"); ComputedStyle computedStyle = new ComputedStyle(element); int[] paddings = computedStyle.getPadding(); + // Some browsers do not reflow until accessing data from the computed + // style object + Profiler.leave("new ComputedStyle"); + + Profiler.enter("Measure paddings"); if (!heightChanged && hasHeightChanged(this.paddings, paddings)) { debugSizeChange(element, "Height (padding)", this.paddings, paddings); @@ -200,7 +207,9 @@ public class MeasuredSize { widthChanged = true; } this.paddings = paddings; + Profiler.leave("Measure paddings"); + Profiler.enter("Measure margins"); int[] margins = computedStyle.getMargin(); if (!heightChanged && hasHeightChanged(this.margins, margins)) { debugSizeChange(element, "Height (margins)", this.margins, margins); @@ -211,7 +220,9 @@ public class MeasuredSize { widthChanged = true; } this.margins = margins; + Profiler.leave("Measure margins"); + Profiler.enter("Measure borders"); int[] borders = computedStyle.getBorder(); if (!heightChanged && hasHeightChanged(this.borders, borders)) { debugSizeChange(element, "Height (borders)", this.borders, borders); @@ -222,7 +233,9 @@ public class MeasuredSize { widthChanged = true; } this.borders = borders; + Profiler.leave("Measure borders"); + Profiler.enter("Measure height"); int requiredHeight = Util.getRequiredHeight(element); int marginHeight = sumHeights(margins); int oldHeight = height; @@ -231,13 +244,18 @@ public class MeasuredSize { debugSizeChange(element, "Height (outer)", oldHeight, height); heightChanged = true; } + Profiler.leave("Measure height"); + Profiler.enter("Measure width"); int requiredWidth = Util.getRequiredWidth(element); int marginWidth = sumWidths(margins); if (setOuterWidth(requiredWidth + marginWidth)) { debugSizeChange(element, "Width (outer)", oldWidth, width); widthChanged = true; } + Profiler.leave("Measure width"); + + Profiler.leave("MeasuredSize.measure"); return new MeasureResult(widthChanged, heightChanged); } diff --git a/client/src/com/vaadin/client/Profiler.java b/client/src/com/vaadin/client/Profiler.java new file mode 100644 index 0000000000..a7b9019d33 --- /dev/null +++ b/client/src/com/vaadin/client/Profiler.java @@ -0,0 +1,365 @@ +/* + * Copyright 2012 Vaadin Ltd. + * + * 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.vaadin.client; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.Map; + +import com.google.gwt.core.client.Duration; +import com.google.gwt.core.client.JavaScriptObject; +import com.google.gwt.core.client.JsArray; +import com.google.gwt.core.shared.GWT; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.Widget; + +/** + * Lightweight profiling tool that can be used to collect profiling data with + * zero overhead unless enabled. To enable profiling, add + * <set-property name="vaadin.profiler" value="true" /> to + * your .gwt.xml file. + * + * @author Vaadin Ltd + * @since 7.0.0 + */ +public class Profiler { + /** + * Class to include using deferred binding to enable the profiling. + * + * @author Vaadin Ltd + * @since 7.0.0 + */ + public static class EnabledProfiler extends Profiler { + @Override + protected boolean isImplEnabled() { + return true; + } + } + + private static JsArray events; + + private static final class ProfilerEvent extends JavaScriptObject { + protected ProfilerEvent() { + // JSO constructor + } + + public native String getName() + /*-{ + return this.name; + }-*/; + + private native double getRawTime() + /*-{ + return this.time; + }-*/; + + private boolean isStart() { + return getRawTime() <= 0; + } + } + + private static class Node { + + private final String name; + private final LinkedHashMap children = new LinkedHashMap(); + private double time = 0; + private int count = 0; + + public Node(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public Node addEvent(ProfilerEvent event) { + Node child = children.get(event.getName()); + if (child == null) { + child = new Node(event.getName()); + children.put(event.getName(), child); + } + child.time += event.getRawTime(); + child.count++; + return child; + } + + public void registerEnd(ProfilerEvent event) { + time += event.getRawTime(); + } + + public double getTimeSpent() { + return time; + } + + public int getCount() { + return count; + } + + public double getOwnTime() { + double time = getTimeSpent(); + for (Node node : children.values()) { + time -= node.getTimeSpent(); + } + return time; + } + + public Widget buildTree() { + String message = getStringRepresentation(""); + + if (getName() == null || !children.isEmpty()) { + SimpleTree tree = new SimpleTree(message); + for (Node node : children.values()) { + Widget child = node.buildTree(); + tree.add(child); + } + return tree; + } else { + return new Label(message); + } + } + + public void buildRecursiveString(StringBuilder builder, String prefix) { + if (getName() != null) { + String msg = getStringRepresentation(prefix); + builder.append(msg + '\n'); + } + String childPrefix = prefix + "*"; + for (Node node : children.values()) { + node.buildRecursiveString(builder, childPrefix); + } + } + + private String getStringRepresentation(String prefix) { + if (getName() == null) { + return ""; + } + String msg = prefix + " " + getName() + " in " + getTimeSpent() + + " ms."; + if (getCount() > 1) { + msg += " Invoked " + + getCount() + + " times (" + + roundToSignificantFigures(getTimeSpent() / getCount()) + + " ms per time)."; + } + if (!children.isEmpty()) { + double ownTime = getOwnTime(); + msg += " " + ownTime + " ms spent in own code"; + if (getCount() > 1) { + msg += " (" + + roundToSignificantFigures(ownTime / getCount()) + + " ms per time)"; + } + msg += '.'; + } + return msg; + } + + public static double roundToSignificantFigures(double num) { + // Number of significant digits + int n = 3; + if (num == 0) { + return 0; + } + + final double d = Math.ceil(Math.log10(num < 0 ? -num : num)); + final int power = n - (int) d; + + final double magnitude = Math.pow(10, power); + final long shifted = Math.round(num * magnitude); + return shifted / magnitude; + } + + public void sumUpTotals(Map totals) { + String name = getName(); + if (name != null) { + Node totalNode = totals.get(name); + if (totalNode == null) { + totalNode = new Node(name); + totals.put(name, totalNode); + } + + totalNode.time += getOwnTime(); + totalNode.count += getCount(); + } + for (Node node : children.values()) { + node.sumUpTotals(totals); + } + } + } + + /** + * Checks whether the profiling gathering is enabled. + * + * @return true if the profiling is enabled, else + * false + */ + public static boolean isEnabled() { + // This will be fully inlined by the compiler + Profiler create = GWT.create(Profiler.class); + return create.isImplEnabled(); + } + + /** + * Enters a named block. There should always be a matching invocation of + * {@link #leave(String)} when leaving the block. Calls to this method will + * be removed by the compiler unless profiling is enabled. + * + * @param name + * the name of the entered block + */ + public static void enter(String name) { + if (isEnabled()) { + pushEvent(events, name, -Duration.currentTimeMillis()); + } + } + + /** + * Leaves a named block. There should always be a matching invocation of + * {@link #enter(String)} when entering the block. Calls to this method will + * be removed by the compiler unless profiling is enabled. + * + * @param name + * the name of the left block + */ + public static void leave(String name) { + if (isEnabled()) { + pushEvent(events, name, Duration.currentTimeMillis()); + } + } + + private static native final void pushEvent(JsArray target, + String name, double time) + /*-{ + target[target.length] = {name: name, time: time}; + }-*/; + + /** + * Resets the collected profiler data. Calls to this method will be removed + * by the compiler unless profiling is enabled. + */ + public static void reset() { + if (isEnabled()) { + events = JavaScriptObject.createArray().cast(); + } + } + + /** + * Outputs the gathered profiling data to the debug console. + */ + public static void logTimings() { + if (!isEnabled()) { + VConsole.log("Profiler is not enabled, no data has been collected."); + return; + } + + LinkedList stack = new LinkedList(); + Node rootNode = new Node(null); + stack.add(rootNode); + for (int i = 0; i < events.length(); i++) { + ProfilerEvent event = events.get(i); + if (event.isStart()) { + Node stackTop = stack.getLast().addEvent(event); + stack.add(stackTop); + } else { + Node stackTop = stack.removeLast(); + if (stackTop == null) { + VConsole.error("Leaving " + event.getName() + + " that was never entered."); + return; + } + if (!stackTop.getName().equals(event.getName())) { + VConsole.error("Invalid profiling event order, leaving " + + event.getName() + " but " + stackTop.getName() + + " was expected"); + return; + } + stackTop.registerEnd(event); + } + } + + if (stack.size() != 1) { + VConsole.log("Not all nodes are left, the last node is " + + stack.getLast().getName()); + return; + } + + StringBuilder stringBuilder = new StringBuilder(); + rootNode.buildRecursiveString(stringBuilder, ""); + Console implementation = VConsole.getImplementation(); + if (implementation instanceof VDebugConsole) { + VDebugConsole console = (VDebugConsole) implementation; + + SimpleTree tree = (SimpleTree) stack.getFirst().buildTree(); + tree.setText("Profiler data"); + + console.showTree(tree, stringBuilder.toString()); + } else { + VConsole.log(stringBuilder.toString()); + } + + Map totals = new HashMap(); + rootNode.sumUpTotals(totals); + + ArrayList totalList = new ArrayList(totals.values()); + Collections.sort(totalList, new Comparator() { + @Override + public int compare(Node o1, Node o2) { + return (int) (o2.getTimeSpent() - o1.getTimeSpent()); + } + }); + + double total = 0; + double top20total = 0; + for (int i = 0; i < totalList.size(); i++) { + Node node = totalList.get(i); + double timeSpent = node.getTimeSpent(); + total += timeSpent; + if (i < 20) { + top20total += timeSpent; + } + } + + VConsole.log("Largest individual contributors using " + top20total + + " ms out of " + total + " ms"); + for (int i = 0; i < 20 && i < totalList.size(); i++) { + Node node = totalList.get(i); + double timeSpent = node.getTimeSpent(); + total += timeSpent; + VConsole.log(" * " + node.getName() + ": " + timeSpent + " ms in " + + node.getCount() + " invokations."); + } + + } + + /** + * Overridden in {@link EnabledProfiler} to make {@link #isEnabled()} return + * true if GWT.create returns that class. + * + * @return true if the profiling is enabled, else + * false + */ + protected boolean isImplEnabled() { + return false; + } + +} diff --git a/client/src/com/vaadin/client/VDebugConsole.java b/client/src/com/vaadin/client/VDebugConsole.java index 2739273fce..ee7505876d 100644 --- a/client/src/com/vaadin/client/VDebugConsole.java +++ b/client/src/com/vaadin/client/VDebugConsole.java @@ -513,6 +513,23 @@ public class VDebugConsole extends VOverlay implements Console { // consoleLog(u.getChildrenAsXML()); } + /** + * Adds a {@link SimpleTree} to the console and prints a string + * representation of the tree structure to the text console. + * + * @param tree + * the simple tree to display in the console window + * @param stringRepresentation + * the string representation of the tree to output to the text + * console + */ + public void showTree(SimpleTree tree, String stringRepresentation) { + if (panel.isAttached()) { + panel.add(tree); + } + consoleLog(stringRepresentation); + } + private static native void consoleDir(ValueMap u) /*-{ if($wnd.console && $wnd.console.log) { diff --git a/client/src/com/vaadin/client/WidgetSet.java b/client/src/com/vaadin/client/WidgetSet.java index 8efdd1dee4..34e18a5e4e 100644 --- a/client/src/com/vaadin/client/WidgetSet.java +++ b/client/src/com/vaadin/client/WidgetSet.java @@ -47,6 +47,7 @@ public class WidgetSet { * some hacks. Extra instantiation code is needed if client side * connector has no "native" counterpart on client side. */ + Profiler.enter("WidgetSet.createConnector"); Class classType = resolveInheritedConnectorType( conf, tag); @@ -56,6 +57,7 @@ public class WidgetSet { UnknownComponentConnector c = GWT .create(UnknownComponentConnector.class); c.setServerSideClassName(serverSideName); + Profiler.leave("WidgetSet.createConnector"); return c; } else { /* @@ -68,8 +70,10 @@ public class WidgetSet { ((HasJavaScriptConnectorHelper) connector) .getJavascriptConnectorHelper().setTag(tag); } + Profiler.leave("WidgetSet.createConnector"); return connector; } catch (NoDataException e) { + Profiler.leave("WidgetSet.createConnector"); throw new IllegalStateException( "There is no information about " + classType diff --git a/client/src/com/vaadin/client/communication/JsonDecoder.java b/client/src/com/vaadin/client/communication/JsonDecoder.java index d4d71b3bc4..e1ee1fd7b7 100644 --- a/client/src/com/vaadin/client/communication/JsonDecoder.java +++ b/client/src/com/vaadin/client/communication/JsonDecoder.java @@ -32,6 +32,7 @@ import com.vaadin.client.ApplicationConnection; import com.vaadin.client.ConnectorMap; import com.vaadin.client.FastStringSet; import com.vaadin.client.JsArrayObject; +import com.vaadin.client.Profiler; import com.vaadin.client.metadata.NoDataException; import com.vaadin.client.metadata.Property; import com.vaadin.client.metadata.Type; @@ -128,20 +129,24 @@ public class JsonDecoder { private static Object decodeObject(Type type, JSONValue jsonValue, Object target, ApplicationConnection connection) { + Profiler.enter("JsonDecoder.decodeObject"); JSONSerializer serializer = (JSONSerializer) type .findSerializer(); if (serializer != null) { if (target != null && serializer instanceof DiffJSONSerializer) { DiffJSONSerializer diffSerializer = (DiffJSONSerializer) serializer; diffSerializer.update(target, type, jsonValue, connection); + Profiler.leave("JsonDecoder.decodeObject"); return target; } else { Object object = serializer.deserialize(type, jsonValue, connection); + Profiler.leave("JsonDecoder.decodeObject"); return object; } } else { try { + Profiler.enter("JsonDecoder.decodeObject meta data processing"); JsArrayObject properties = type .getPropertiesAsArray(); if (target == null) { @@ -167,12 +172,18 @@ public class JsonDecoder { propertyReference = null; } + Profiler.leave("JsonDecoder.decodeObject meta data processing"); Object decodedValue = decodeValue(propertyType, encodedPropertyValue, propertyReference, connection); + Profiler.enter("JsonDecoder.decodeObject meta data processing"); property.setValue(target, decodedValue); } + Profiler.leave("JsonDecoder.decodeObject meta data processing"); + Profiler.leave("JsonDecoder.decodeObject"); return target; } catch (NoDataException e) { + Profiler.leave("JsonDecoder.decodeObject meta data processing"); + Profiler.leave("JsonDecoder.decodeObject"); throw new RuntimeException("Can not deserialize " + type.getSignature(), e); } diff --git a/client/src/com/vaadin/client/ui/AbstractConnector.java b/client/src/com/vaadin/client/ui/AbstractConnector.java index f9c74cc5c9..898b8cc483 100644 --- a/client/src/com/vaadin/client/ui/AbstractConnector.java +++ b/client/src/com/vaadin/client/ui/AbstractConnector.java @@ -29,6 +29,7 @@ import com.google.gwt.event.shared.HandlerManager; import com.google.web.bindery.event.shared.HandlerRegistration; import com.vaadin.client.ApplicationConnection; import com.vaadin.client.FastStringMap; +import com.vaadin.client.Profiler; import com.vaadin.client.ServerConnector; import com.vaadin.client.Util; import com.vaadin.client.VConsole; @@ -110,11 +111,19 @@ public abstract class AbstractConnector implements ServerConnector, @Override public final void doInit(String connectorId, ApplicationConnection connection) { + Profiler.enter("AbstractConnector.doInit"); this.connection = connection; id = connectorId; addStateChangeHandler(this); + if (Profiler.isEnabled()) { + Profiler.enter("AbstractConnector.init " + Util.getSimpleName(this)); + } init(); + if (Profiler.isEnabled()) { + Profiler.leave("AbstractConnector.init " + Util.getSimpleName(this)); + } + Profiler.leave("AbstractConnector.doInit"); } /** @@ -200,6 +209,12 @@ public abstract class AbstractConnector implements ServerConnector, @Override public void fireEvent(GwtEvent event) { + String profilerKey = null; + if (Profiler.isEnabled()) { + profilerKey = "Fire " + Util.getSimpleName(event) + " for " + + Util.getSimpleName(this); + Profiler.enter(profilerKey); + } if (handlerManager != null) { handlerManager.fireEvent(event); } @@ -214,6 +229,10 @@ public abstract class AbstractConnector implements ServerConnector, } } } + if (Profiler.isEnabled()) { + Profiler.leave(profilerKey); + } + } protected HandlerManager ensureHandlerManager() { @@ -263,6 +282,7 @@ public abstract class AbstractConnector implements ServerConnector, @Override public void onStateChanged(StateChangeEvent stateChangeEvent) { + Profiler.enter("AbstractConnector.onStateChanged"); if (debugLogging) { VConsole.log("State change event for " + Util.getConnectorString(stateChangeEvent.getConnector()) @@ -270,6 +290,7 @@ public abstract class AbstractConnector implements ServerConnector, } updateEnabledState(isEnabled()); + Profiler.leave("AbstractConnector.onStateChanged"); } /* @@ -296,7 +317,9 @@ public abstract class AbstractConnector implements ServerConnector, @Override public SharedState getState() { if (state == null) { + Profiler.enter("AbstractConnector.createState()"); state = createState(); + Profiler.leave("AbstractConnector.createState()"); } return state; diff --git a/client/src/com/vaadin/client/ui/dd/VDragAndDropManager.java b/client/src/com/vaadin/client/ui/dd/VDragAndDropManager.java index 280e44a9b5..ffc146ad04 100644 --- a/client/src/com/vaadin/client/ui/dd/VDragAndDropManager.java +++ b/client/src/com/vaadin/client/ui/dd/VDragAndDropManager.java @@ -37,6 +37,7 @@ import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ApplicationConnection; import com.vaadin.client.ComponentConnector; import com.vaadin.client.MouseEventDetailsBuilder; +import com.vaadin.client.Profiler; import com.vaadin.client.UIDL; import com.vaadin.client.Util; import com.vaadin.client.VConsole; @@ -659,6 +660,8 @@ public class VDragAndDropManager { if (serverCallback == null) { return; } + Profiler.enter("VDragAndDropManager.handleServerResponse"); + UIDL uidl = (UIDL) valueMap.cast(); int visitId = uidl.getIntAttribute("visitId"); @@ -668,6 +671,8 @@ public class VDragAndDropManager { serverCallback = null; } runDeferredCommands(); + + Profiler.leave("VDragAndDropManager.handleServerResponse"); } private void runDeferredCommands() { diff --git a/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java b/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java index 77def89e9e..50de8e0936 100644 --- a/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java +++ b/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java @@ -23,6 +23,7 @@ import com.vaadin.client.ApplicationConnection; import com.vaadin.client.ComponentConnector; import com.vaadin.client.ConnectorHierarchyChangeEvent; import com.vaadin.client.LayoutManager; +import com.vaadin.client.Profiler; import com.vaadin.client.ServerConnector; import com.vaadin.client.Util; import com.vaadin.client.communication.StateChangeEvent; @@ -281,6 +282,7 @@ public abstract class AbstractOrderedLayoutConnector extends */ @Override public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) { + Profiler.enter("AOLC.onConnectorHierarchyChange"); List previousChildren = event.getOldChildren(); int currentIndex = 0; @@ -289,14 +291,22 @@ public abstract class AbstractOrderedLayoutConnector extends layout.setSpacing(getState().spacing); for (ComponentConnector child : getChildComponents()) { + Profiler.enter("AOLC.onConnectorHierarchyChange add children"); Slot slot = layout.getSlot(child.getWidget()); if (slot.getParent() != layout) { + Profiler.enter("AOLC.onConnectorHierarchyChange add state change handler"); child.addStateChangeHandler(childStateChangeHandler); + Profiler.leave("AOLC.onConnectorHierarchyChange add state change handler"); } + Profiler.enter("AOLC.onConnectorHierarchyChange addOrMoveSlot"); layout.addOrMoveSlot(slot, currentIndex++); + Profiler.leave("AOLC.onConnectorHierarchyChange addOrMoveSlot"); + + Profiler.leave("AOLC.onConnectorHierarchyChange add children"); } for (ComponentConnector child : previousChildren) { + Profiler.enter("AOLC.onConnectorHierarchyChange remove children"); if (child.getParent() != this) { Slot slot = layout.getSlot(child.getWidget()); slot.setWidgetResizeListener(null); @@ -309,7 +319,9 @@ public abstract class AbstractOrderedLayoutConnector extends child.removeStateChangeHandler(childStateChangeHandler); layout.removeWidget(child.getWidget()); } + Profiler.leave("AOL.onConnectorHierarchyChange remove children"); } + Profiler.leave("AOLC.onConnectorHierarchyChange"); updateInternalState(); } @@ -342,6 +354,7 @@ public abstract class AbstractOrderedLayoutConnector extends if (processedResponseId == lastResponseId) { return; } + Profiler.enter("AOLC.updateInternalState"); // Remember that everything is updated for this response processedResponseId = lastResponseId; @@ -422,6 +435,8 @@ public abstract class AbstractOrderedLayoutConnector extends } else { getWidget().clearExpand(); } + + Profiler.leave("AOLC.updateInternalState"); } /**