All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vaadin.client.Profiler Maven / Gradle / Ivy

Go to download

Vaadin is a web application framework for Rich Internet Applications (RIA). Vaadin enables easy development and maintenance of fast and secure rich web applications with a stunning look and feel and a wide browser support. It features a server-side architecture with the majority of the logic running on the server. Ajax technology is used at the browser-side to ensure a rich and interactive user experience.

There is a newer version: 8.27.1
Show newest version
/*
 * Copyright 2000-2013 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 java.util.logging.Logger;

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.vaadin.client.debug.internal.ProfilerSection.Node;
import com.vaadin.client.debug.internal.ProfilerSection.ProfilerResultConsumer;

/**
 * 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 final String evtGroup = "VaadinProfiler";

    private static final class GwtStatsEvent extends JavaScriptObject {
        protected GwtStatsEvent() {
            // JSO constructor
        }

        private native String getEvtGroup()
        /*-{
            return this.evtGroup;
        }-*/;

        private native double getMillis()
        /*-{
            return this.millis;
        }-*/;

        private native String getSubSystem()
        /*-{
            return this.subSystem;
        }-*/;

        private native String getType()
        /*-{
            return this.type;
        }-*/;

        private native String getModuleName()
        /*-{
            return this.moduleName;
        }-*/;

        public final String getEventName() {
            String group = getEvtGroup();
            if (evtGroup.equals(group)) {
                return getSubSystem();
            } else {
                return group + "." + getSubSystem();
            }
        }
    }

    private static ProfilerResultConsumer consumer;

    /**
     * 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()) {
            logGwtEvent(name, "begin");
        }
    }

    /**
     * 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()) {
            logGwtEvent(name, "end");
        }
    }

    private static native final void logGwtEvent(String name, String type)
    /*-{
        $wnd.__gwtStatsEvent({
            evtGroup: @com.vaadin.client.Profiler::evtGroup,
            moduleName: @com.google.gwt.core.client.GWT::getModuleName()(),
            millis: (new Date).getTime(),
            sessionId: undefined,
            subSystem: name,
            type: type
        });
    }-*/;

    /**
     * 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()) {
            /*
             * Old implementations might call reset for initialization, so
             * ensure it is initialized here as well. Initialization has no side
             * effects if already done.
             */
            initialize();

            clearEventsList();
        }
    }

    /**
     * Initializes the profiler. This should be done before calling any other
     * function in this class. Failing to do so might cause undesired behavior.
     * This method has no side effects if the initialization has already been
     * done.
     * 

* Please note that this method should be called even if the profiler is not * enabled because it will then remove a logger function that might have * been included in the HTML page and that would leak memory unless removed. *

* * @since 7.0.2 */ public static void initialize() { if (isEnabled()) { ensureLogger(); } else { ensureNoLogger(); } } /** * Outputs the gathered profiling data to the debug console. */ public static void logTimings() { if (!isEnabled()) { getLogger().warning( "Profiler is not enabled, no data has been collected."); return; } LinkedList stack = new LinkedList(); Node rootNode = new Node(null); stack.add(rootNode); JsArray gwtStatsEvents = getGwtStatsEvents(); if (gwtStatsEvents.length() == 0) { getLogger() .warning( "No profiling events recorded, this might happen if another __gwtStatsEvent handler is installed."); return; } for (int i = 0; i < gwtStatsEvents.length(); i++) { GwtStatsEvent gwtStatsEvent = gwtStatsEvents.get(i); String eventName = gwtStatsEvent.getEventName(); String type = gwtStatsEvent.getType(); boolean isBeginEvent = "begin".equals(type); Node stackTop = stack.getLast(); boolean inEvent = eventName.equals(stackTop.getName()) && !isBeginEvent; if (!inEvent && stack.size() >= 2 && eventName.equals(stack.get(stack.size() - 2).getName()) && !isBeginEvent) { // back out of sub event stackTop.addTime(gwtStatsEvent.getMillis()); stack.removeLast(); stackTop = stack.getLast(); inEvent = true; } if (type.equals("end")) { if (!inEvent) { getLogger().severe( "Got end event for " + eventName + " but is currently in " + stackTop.getName()); return; } Node previousStackTop = stack.removeLast(); previousStackTop.addTime(gwtStatsEvent.getMillis()); } else { if (!inEvent) { stackTop = stackTop.enterChild(eventName, gwtStatsEvent.getMillis()); stack.add(stackTop); } if (!isBeginEvent) { // Create sub event stack.add(stackTop.enterChild(eventName + "." + type, gwtStatsEvent.getMillis())); } } } if (stack.size() != 1) { getLogger().warning( "Not all nodes are left, the last node is " + stack.getLast().getName()); return; } 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()); } }); getConsumer().addProfilerData(stack.getFirst(), totalList); } /** * 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; } /** * Outputs the time passed since various events recored in * performance.timing if supported by the browser. */ public static void logBootstrapTimings() { if (isEnabled()) { double now = Duration.currentTimeMillis(); StringBuilder stringBuilder = new StringBuilder( "Time since window.performance.timing events"); SimpleTree tree = new SimpleTree(stringBuilder.toString()); String[] keys = new String[] { "navigationStart", "unloadEventStart", "unloadEventEnd", "redirectStart", "redirectEnd", "fetchStart", "domainLookupStart", "domainLookupEnd", "connectStart", "connectEnd", "requestStart", "responseStart", "responseEnd", "domLoading", "domInteractive", "domContentLoadedEventStart", "domContentLoadedEventEnd", "domComplete", "loadEventStart", "loadEventEnd" }; LinkedHashMap timings = new LinkedHashMap(); for (String key : keys) { double value = getPerformanceTiming(key); if (value == 0) { // Ignore missing value continue; } timings.put(key, Double.valueOf(now - value)); } if (timings.isEmpty()) { getLogger() .info("Bootstrap timings not supported, please ensure your browser supports performance.timing"); return; } getConsumer().addBootstrapData(timings); } } private static final native double getPerformanceTiming(String name) /*-{ if ($wnd.performance && $wnd.performance.timing && $wnd.performance.timing[name]) { return $wnd.performance.timing[name]; } else { return 0; } }-*/; private static native JsArray getGwtStatsEvents() /*-{ return $wnd.vaadin.gwtStatsEvents || []; }-*/; /** * Add logger if it's not already there, also initializing the event array * if needed. */ private static native void ensureLogger() /*-{ if (typeof $wnd.__gwtStatsEvent != 'function') { if (typeof $wnd.vaadin.gwtStatsEvents != 'object') { $wnd.vaadin.gwtStatsEvents = []; } $wnd.__gwtStatsEvent = function(event) { $wnd.vaadin.gwtStatsEvents.push(event); return true; } } }-*/; /** * Remove logger function and event array if it seems like the function has * been added by us. */ private static native void ensureNoLogger() /*-{ if (typeof $wnd.vaadin.gwtStatsEvents == 'object') { delete $wnd.vaadin.gwtStatsEvents; if (typeof $wnd.__gwtStatsEvent == 'function') { $wnd.__gwtStatsEvent = function(){}; } } }-*/; private static native JsArray clearEventsList() /*-{ $wnd.vaadin.gwtStatsEvents = []; }-*/; /** * Sets the profiler result consumer that is used to output the profiler * data to the user. *

* Warning! This is internal API and should not be used by * applications or add-ons. * * @since 7.1 * @param profilerResultConsumer * the consumer that gets profiler data */ public static void setProfilerResultConsuer( ProfilerResultConsumer profilerResultConsumer) { if (consumer != null) { throw new IllegalStateException("The consumer has already been set"); } consumer = profilerResultConsumer; } private static ProfilerResultConsumer getConsumer() { if (consumer == null) { throw new IllegalStateException("No consumer has been registered"); } else { return consumer; } } private static Logger getLogger() { return Logger.getLogger(Profiler.class.getName()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy