Package com.google.speedtracer.client.visualizations.view

Source Code of com.google.speedtracer.client.visualizations.view.EventWaterfall$Resources

/*
* 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.speedtracer.client.visualizations.view;

import com.google.gwt.coreext.client.JSOArray;
import com.google.gwt.coreext.client.JsIntegerDoubleMap;
import com.google.gwt.coreext.client.JsIntegerMap;
import com.google.gwt.coreext.client.JsIntegerDoubleMap.IterationCallBack;
import com.google.gwt.graphics.client.Color;
import com.google.gwt.topspin.ui.client.ClickEvent;
import com.google.gwt.topspin.ui.client.ClickListener;
import com.google.gwt.topspin.ui.client.Container;
import com.google.gwt.topspin.ui.client.MouseOverEvent;
import com.google.gwt.topspin.ui.client.MouseOverListener;
import com.google.speedtracer.client.SymbolServerController;
import com.google.speedtracer.client.SymbolServerService;
import com.google.speedtracer.client.model.ButtonDescription;
import com.google.speedtracer.client.model.GraphCalloutModel;
import com.google.speedtracer.client.model.LogEvent;
import com.google.speedtracer.client.model.UiEvent;
import com.google.speedtracer.client.model.UiEventDispatcher;
import com.google.speedtracer.client.util.Url;
import com.google.speedtracer.client.visualizations.model.SluggishnessModel;
import com.google.speedtracer.client.visualizations.model.SluggishnessVisualization;
import com.google.speedtracer.client.visualizations.view.SluggishnessDetailView.EventWaterfallFilter;

import java.util.List;

/**
* The waterfall of DOM/UI Events shown on the sluggishness graph.
*/
public class EventWaterfall extends FilteringScrollTable {
  /**
   * Externalized Resources.
   */
  public interface Resources extends FilteringScrollTable.Resources,
      EventWaterfallRow.Resources, SluggishnessEventFilterPanel.Resources {
  }

  /**
   * Handles events for a row.
   */
  private interface RowListener extends MouseOverListener, ClickListener {
  }

  /**
   * TODO(knorton): This class was simply extracted from EventTraceBreakdown but
   * there are obvious refactorings and cleanups to be made in another pass. I
   * plan to simplify this class considerably.
   *
   * NOTES:
   * <p>
   * An event/sub-event gets marked with a dominant type if it's important, but
   * small. Importance is determined by whether the node has children who are
   * part of the set of types that exceed a threshold. Or, if it's a log
   * message. :-) The dominant type is computed by descending a sub-tree and
   * computing a type-duration map for every node.
   * </p>
   */
  private static class Presenter implements LazyEventTree.Presenter {
    /**
     * Simply utility class for aggregating information in two
     * JsIntegerDoubleMaps by iterating over one of them.
     */
    class TypeMapAggregator implements IterationCallBack {
      double maxValue = 0;
      JsIntegerDoubleMap parentTypeMap;
      int typeOfMax;

      public TypeMapAggregator(JsIntegerDoubleMap parentTypeMap) {
        this.parentTypeMap = parentTypeMap;
      }

      public void onIteration(int key, double val) {
        double value = ((parentTypeMap.hasKey(key)) ? parentTypeMap.get(key)
            + val : val);
        parentTypeMap.put(key, value);
        maybeChangeMax(key, value);
      }

      private void maybeChangeMax(int key, double val) {
        if (val >= maxValue) {
          typeOfMax = key;
          maxValue = val;
        }
      }
    }

    private static final int SIGNIFICANCE_IN_PIXELS = 1;
    private static final int AGGREGATE_SIGNIFICANCE_IN_PIXELS = 5;

    private static native Color getDominantColor(UiEvent event) /*-{
      return event.dominantColor;
    }-*/;

    private static native void setDominantColor(UiEvent event, Color color) /*-{
      event.dominantColor = color;
    }-*/;

    public Color getColor(UiEvent event) {
      return EventRecordColors.getColorForType(event.getType());
    }

    public Color getDominantTypeColor(UiEvent event) {
      return getDominantColor(event);
    }

    public double getInsignificanceThreshold(double msPerPixel) {
      return SIGNIFICANCE_IN_PIXELS / msPerPixel;
    }

    public String getLabel(UiEvent event) {
      return UiEvent.typeToDetailedTypeString(event);
    }

    public boolean hasDominantType(UiEvent event, UiEvent rootEvent,
        double msPerPixel) {
      // We now attempt to associate a dominant type (color) with this tiny
      // node. We do not want to have exponential search behavior. So we
      // computeDominantColorForSubtree will memoize its results.
      computeDominantColorForSubtree(event, msPerPixel, rootEvent);
      return getDominantColor(event) != null;
    }

    /**
     * Post order traversal. At each visit, we know that the typeMap for all our
     * children should be up to date for that subtree. We simply update our own
     * typemap with the information contained in the children's typeMap, and
     * then set our own dominant color if it matters.
     */
    private void computeDominantColorForSubtree(UiEvent node,
        double msPerPixel, UiEvent rootEvent) {
      if (isMarked(node)) {
        return;
      }

      JsIntegerDoubleMap typeMap = JsIntegerDoubleMap.create();
      JSOArray<UiEvent> children = node.getChildren();
      // Leaf node check
      if (children.isEmpty()) {
        markNode(node);
        final int nodeType = node.getType();
        typeMap.put(nodeType, node.getSelfTime());
        // Set the typemap for parent nodes to use in their calculations.
        node.setTypeDurations(typeMap);
        setDominantColorIfImportant(node, nodeType, msPerPixel, rootEvent);
        return;
      }

      // Recursive call
      for (int i = 0, n = children.size(); i < n; i++) {
        computeDominantColorForSubtree(children.get(i), msPerPixel, rootEvent);
      }

      // Visit the node.
      // A Visit includes an iteration over the children to aggregate their type
      // map information into our own. And then figuring out what the dominant
      // type is.
      markNode(node);
      TypeMapAggregator aggregator = new TypeMapAggregator(typeMap);
      for (int i = 0, n = children.size(); i < n; i++) {
        children.get(i).getTypeDurations().iterate(aggregator);
      }

      // Set the typemap for parent nodes to use in their calculations.
      node.setTypeDurations(typeMap);
      setDominantColorIfImportant(node, aggregator.typeOfMax, msPerPixel,
          rootEvent);
    }

    /**
     * Checks to see if a node has been visited during the dominant color
     * computation.
     */
    private native boolean isMarked(UiEvent node) /*-{
      return !!node.typeBreakdownDone;
    }-*/;

    /**
     * Marks a node as visited during the dominant color computation.
     */
    private native void markNode(UiEvent node) /*-{
      node.typeBreakdownDone = true;
    }-*/;

    private void setDominantColorIfImportant(UiEvent node, int dominantType,
        double msPerPixel, UiEvent rootEvent) {
      final double aggregateThreshold = AGGREGATE_SIGNIFICANCE_IN_PIXELS
          / msPerPixel;
      // We check to see if this insignificant thing is part of something
      // significant.
      JsIntegerDoubleMap aggregateTimes = rootEvent.getTypeDurations();
      // Visitors should already have run
      assert aggregateTimes != null : "aggregateTimes is null";

      // Find the dominant color for this node, and if it belongs to an
      // important color, then set the dominant color on the UiEvent.
      if (aggregateTimes.hasKey(dominantType)
          && (aggregateTimes.get(dominantType) >= aggregateThreshold)
          || (node.getType() == LogEvent.TYPE)) {
        setDominantColor(node, EventRecordColors.getColorForType(dominantType));
      }
    }
  }

  // The index of the first event in the table model.
  private int beginIndex = 0;

  // The index of the last event in the table model.
  private int endIndex = 0;

  private SluggishnessEventFilterPanel eventFilterPanel;

  // A map of all rows in the table keyed by the record sequence number
  private JsIntegerMap<EventWaterfallRow> recordMap = JsIntegerMap.createObject().cast();

  private final EventWaterfallRow.Resources resources;

  private RowListener rowListener;

  private final UiEventDispatcher sourceDispatcher;

  private final SluggishnessVisualization visualization;

  private final Presenter presenter = new Presenter();

  public EventWaterfall(Container container, EventWaterfallFilter filter,
      final SluggishnessVisualization visualization,
      UiEventDispatcher sourceDispatcher, EventWaterfall.Resources resources) {
    super(container, filter, resources);

    this.resources = resources;
    this.visualization = visualization;
    this.sourceDispatcher = sourceDispatcher;

    rowListener = new RowListener() {
      public void onClick(ClickEvent event) {
        TableRow row = (TableRow) event.getSource();
        row.toggleDetails();
        EventWaterfallRowDetails details = (EventWaterfallRowDetails) row.getDetails();
        if (details.isJavaScriptProfileInProgress()) {
          details.updateProfile();
        }
      }

      public void onMouseOver(MouseOverEvent event) {
        UiEvent e = (UiEvent) event.getSource();
        assert e != null;
        GraphCalloutModel calloutModel = getVisualization().getTimeline().getModel().getGraphCalloutModel();
        calloutModel.update(e.getTime(), e.getDuration(),
            UiEvent.typeToDetailedTypeString(e), 0, true);
      }
    };

    // Create the EventFilterPanel UI.
    eventFilterPanel = new SluggishnessEventFilterPanel(
        getFilterPanelContainer(), this, filter.eventFilter, resources,
        visualization.getModel());

    // Add a ButtonDescription entry for the control that will open/close the
    // EventFilterPanel.
    visualization.addButton(new ButtonDescription("Open/Close Filter Panel",
        resources.sluggishnessFiletPanelCss().filterPanelButton(),
        new ClickListener() {
          public void onClick(ClickEvent event) {
            eventFilterPanel.refresh(visualization.getModel());
            toggleFilterPanelVisible();
          }
        }));
  }

  /**
   * This method adds a row to the table for a given UiEvent.
   *
   * @param e the event
   * @param append to append or not to append
   */
  public TableRow addRowForUiEvent(UiEvent e, boolean append) {
    EventWaterfallRow row = append ? appendRow(e) : prependRow(e);
    row.createDetails();

    recordMap.put(e.getSequence(), row);

    // Add mouse over listener
    row.addMouseOverListener(e, rowListener);
    row.addClickListener(row, rowListener);

    return row;
  }

  public EventWaterfallRow appendRow(UiEvent uiEvent) {
    EventWaterfallRow row = new EventWaterfallRow(this, uiEvent, resources);
    insertRow(row, true);
    return row;
  }

  public UiEventDispatcher getSourceDispatcher() {
    return sourceDispatcher;
  }

  public SluggishnessVisualization getVisualization() {
    return visualization;
  }

  public EventWaterfallRow prependRow(UiEvent uiEvent) {
    EventWaterfallRow row = new EventWaterfallRow(this, uiEvent, resources);
    insertRow(row, false);
    return row;
  }

  public void refreshRecord(UiEvent uiEvent) {
    EventWaterfallRow row = this.recordMap.get(uiEvent.getSequence());
    if (row == null) {
      return;
    }
    row.getTitleCell().refresh();
    ((EventWaterfallRowDetails) row.getDetails()).refresh();
  }

  /**
   * Wipes the table and re-adds events in the current page.
   */
  @Override
  public void renderTable() {
    // Cancel any pending resymbolization requests.
    // Add resymbolized frame if it is available.
    SymbolServerController ssController = getCurrentSymbolServerController();
    if (ssController != null) {
      ssController.cancelPendingRequests();
    }

    // Clear out the rows.
    clearTable();

    // Clear the map of record number to table rows
    recordMap = JsIntegerMap.createObject().cast();
    List<UiEvent> eventList = sourceDispatcher.getEventList();
    for (int i = beginIndex; i < endIndex; i++) {
      addRowForUiEvent(eventList.get(i), true);
    }

    // Actually add the rows to the dom.
    super.renderTable();
  }

  public void setEndIndex(int endIndex) {
    this.endIndex = endIndex;
  }

  /**
   * Sets the total range of events viewable by the table. Resets the view to
   * the first page.
   *
   * @param beginIndex
   * @param endIndex
   */
  public void updateTotalTableRange(int beginIndex, int endIndex) {
    this.beginIndex = beginIndex;
    this.setEndIndex(endIndex);
    renderTable();
  }

  LazyEventTree.Presenter getPresenter() {
    return presenter;
  }

  private SymbolServerController getCurrentSymbolServerController() {
    SluggishnessModel sModel = (SluggishnessModel) getVisualization().getModel();
    String resourceUrl = sModel.getDataDispatcher().getTabDescription().getUrl();
    return SymbolServerService.getSymbolServerController(new Url(resourceUrl));
  }
}
TOP

Related Classes of com.google.speedtracer.client.visualizations.view.EventWaterfall$Resources

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.