Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / trace-viewer / trace_viewer / tracing / time_summary_side_panel.js
index 45dba0e..9a31cbe 100644 (file)
 
 'use strict';
 
+tvcm.require('tracing.analysis.util');
+tvcm.require('tracing.selection');
 tvcm.require('tracing.timeline_view_side_panel');
 tvcm.require('tvcm.iteration_helpers');
-tvcm.require('tvcm.ui.pie_chart');
+tvcm.require('tvcm.statistics');
 tvcm.require('tvcm.ui.dom_helpers');
+tvcm.require('tvcm.ui.pie_chart');
 
 tvcm.requireTemplate('tracing.time_summary_side_panel');
 
 tvcm.exportTo('tracing', function() {
   var ThreadSlice = tracing.trace_model.ThreadSlice;
 
-  var OVERHEAD_TRACE_CATEGORY = "trace_event_overhead"
-  var OVERHEAD_TRACE_NAME = "overhead"
+  var OVERHEAD_TRACE_CATEGORY = 'trace_event_overhead';
+  var OVERHEAD_TRACE_NAME = 'overhead';
+
+  var tsRound = tracing.analysis.tsRound;
+
+  var RequestSelectionChangeEvent = tracing.RequestSelectionChangeEvent;
 
   function getWallTimeOverheadForEvent(event) {
     if (event.category == OVERHEAD_TRACE_CATEGORY &&
         event.name == OVERHEAD_TRACE_NAME) {
-      return event.duration
+      return event.duration;
     }
-    return 0
+    return 0;
   }
 
   function getCpuTimeOverheadForEvent(event) {
     if (event.category == OVERHEAD_TRACE_CATEGORY &&
-        event.threadDuration) {
-      return event.threadDuration
+        event.cpuDuration) {
+      return event.cpuDuration;
     }
-    return 0
+    return 0;
   }
 
-  function getSlicesInsideRange(filterRange, slices) {
-    var slicesInFilterRange = []
+  function getSlicesIntersectingRange(rangeOfInterest, slices) {
+    var slicesInFilterRange = [];
     for (var i = 0; i < slices.length; i++) {
       var slice = slices[i];
-      if (filterRange.containsExplicitRange(slice.start, slice.end))
-        slicesInFilterRange.push(slice)
+      if (rangeOfInterest.intersectsExplicitRange(slice.start, slice.end))
+        slicesInFilterRange.push(slice);
     }
     return slicesInFilterRange;
   }
 
   /**
+   * This function takes an array of groups and merges smaller groups into the
+   * provided 'Other' group item such that the remaining items are ready for
+   * pie-chart consumption. Otherwise, the pie chart gets overwhelmed with tons
+   * of little slices.
+   */
+  function trimPieChartData(groups, otherGroup, getValue) {
+    // Copy the array so it can be mutated.
+    groups = groups.filter(function(d) {
+      return getValue(d) != 0;
+    });
+
+    // Figure out total array range.
+    var sum = tvcm.Statistics.sum(groups, getValue);
+
+    // Sort by value.
+    function compareByValue(a, b) {
+      return getValue(a) - getValue(b);
+    }
+    groups.sort(compareByValue);
+
+    // Now start fusing elements until none are less than threshold in size.
+    var thresshold = 0.05 * sum;
+    while (groups.length > 1) {
+      var group = groups[0];
+      if (getValue(group) >= thresshold)
+        break;
+
+      var v = getValue(group);
+      if (v + getValue(otherGroup) > thresshold)
+        break;
+
+      // Remove the group from the list and add it to the 'Other' group.
+      groups.splice(0, 1);
+      otherGroup.appendGroupContents(group);
+    }
+
+    // Final return.
+    if (getValue(otherGroup) > 0)
+      groups.push(otherGroup);
+
+    groups.sort(compareByValue);
+
+    return groups;
+  }
+
+  function createPieChartFromResultGroups(groups, title, getValue) {
+    var chart = new tvcm.ui.PieChart();
+
+    // Build chart data.
+    var data = [];
+    groups.forEach(function(resultsForGroup) {
+      var value = getValue(resultsForGroup);
+      if (value === 0)
+        return;
+      data.push({
+        label: resultsForGroup.name,
+        value: value,
+        valueText: tsRound(value) + 'ms',
+        onClick: function() {
+          var event = new tracing.RequestSelectionChangeEvent();
+          event.selection = new tracing.Selection(resultsForGroup.allSlices);
+          event.selection.timeSummaryGroupName = resultsForGroup.name;
+          chart.dispatchEvent(event);
+        }
+      });
+    });
+
+    chart.chartTitle = title;
+    chart.data = data;
+    return chart;
+  }
+
+
+  /**
    * @constructor
    */
   function ResultsForGroup(model, name) {
@@ -55,12 +136,9 @@ tvcm.exportTo('tracing', function() {
 
   ResultsForGroup.prototype = {
     get wallTime() {
-      var wallSum = tvcm.sum(function(x) { return x.duration; },
-                             this.topLevelSlices);
-      var wallOverheadSum = tvcm.sum(function(x) {
-        return getWallTimeOverheadForEvent(x);
-      },this.allSlices);
-      return wallSum - wallOverheadSum;
+      var wallSum = tvcm.Statistics.sum(
+          this.topLevelSlices, function(x) { return x.duration; });
+      return wallSum;
     },
 
     get cpuTime() {
@@ -71,32 +149,41 @@ tvcm.exportTo('tracing', function() {
         //
         // A thread_duration of 0 is valid, so this only returns 0 if it is
         // None.
-        if (x.threadDuration === undefined) {
+        if (x.cpuDuration === undefined) {
           if (x.duration === undefined)
-            continue
+            continue;
           return 0;
         } else {
-          cpuDuration += x.threadDuration
+          cpuDuration += x.cpuDuration;
         }
       }
 
-      var cpuOverhead = tvcm.sum(function(x) {
-        return getCpuTimeOverheadForEvent(x);
-      }, this.allSlices);
-      return cpuDuration - cpuOverhead
+      return cpuDuration;
     },
 
-    appendThreadSlices: function(filterRange, thread) {
-      getSlicesInsideRange(
-          filterRange, thread.sliceGroup.slices).forEach(
-              function(slice) {
-                this.allSlices.push(slice);
-              }, this);
-      getSlicesInsideRange(
-          filterRange, thread.sliceGroup.topLevelSlices).forEach(
-              function(slice) {
-                this.topLevelSlices.push(slice);
-              }, this);
+    appendGroupContents: function(group) {
+      if (group.model != this.model)
+        throw new Error('Models must be the same');
+
+      group.allSlices.forEach(function(slice) {
+        this.allSlices.push(slice);
+      }, this);
+      group.topLevelSlices.forEach(function(slice) {
+        this.topLevelSlices.push(slice);
+      }, this);
+    },
+
+    appendThreadSlices: function(rangeOfInterest, thread) {
+      var tmp = getSlicesIntersectingRange(
+          rangeOfInterest, thread.sliceGroup.slices);
+      tmp.forEach(function(slice) {
+        this.allSlices.push(slice);
+      }, this);
+      tmp = getSlicesIntersectingRange(
+          rangeOfInterest, thread.sliceGroup.topLevelSlices);
+      tmp.forEach(function(slice) {
+        this.topLevelSlices.push(slice);
+      }, this);
     }
   };
 
@@ -127,23 +214,26 @@ tvcm.exportTo('tracing', function() {
       this.appendChild(tvcm.instantiateTemplate(
           '#x-time-summary-side-panel-template'));
 
+      this.rangeOfInterest_ = new tvcm.Range();
+      this.selection_ = undefined;
       this.groupBy_ = GROUP_BY_PROCESS_NAME;
+      this.chart_ = undefined;
 
       var toolbarEl = this.querySelector('toolbar');
       toolbarEl.appendChild(tvcm.ui.createSelector(
-        this, 'groupBy',
-        'timeSummarySidePanel.groupBy', this.groupBy_,
-        [{label: 'Group by process', value: GROUP_BY_PROCESS_NAME},
-         {label: 'Group by thread', value: GROUP_BY_THREAD_NAME}
-         ]));
+          this, 'groupBy',
+          'timeSummarySidePanel.groupBy', this.groupBy_,
+          [{label: 'Group by process', value: GROUP_BY_PROCESS_NAME},
+           {label: 'Group by thread', value: GROUP_BY_THREAD_NAME}
+          ]));
 
-      this.groupingUnit_ = WALL_TIME_GROUPING_UNIT;
+      this.groupingUnit_ = CPU_TIME_GROUPING_UNIT;
       toolbarEl.appendChild(tvcm.ui.createSelector(
-        this, 'groupingUnit',
-        'timeSummarySidePanel.groupingUnit', this.groupingUnit_,
-        [{label: 'Wall time', value: WALL_TIME_GROUPING_UNIT},
-         {label: 'CPU time', value: CPU_TIME_GROUPING_UNIT}
-         ]));
+          this, 'groupingUnit',
+          'timeSummarySidePanel.groupingUnit', this.groupingUnit_,
+          [{label: 'Wall time', value: WALL_TIME_GROUPING_UNIT},
+           {label: 'CPU time', value: CPU_TIME_GROUPING_UNIT}
+          ]));
     },
 
     get model() {
@@ -175,7 +265,7 @@ tvcm.exportTo('tracing', function() {
 
     getGroupNameForThread_: function(thread) {
       if (this.groupBy_ == GROUP_BY_THREAD_NAME)
-        return thread.name;
+        return thread.name ? thread.name : thread.userFriendlyName;
 
       if (this.groupBy_ == GROUP_BY_PROCESS_NAME)
         return thread.parent.userFriendlyName;
@@ -183,15 +273,19 @@ tvcm.exportTo('tracing', function() {
 
     updateContents_: function() {
       var resultArea = this.querySelector('result-area');
+      this.chart_ = undefined;
       resultArea.textContent = '';
 
       if (this.model_ === undefined)
         return;
 
-      // TODO(nduca): Use the timeline view's interest region
-      // for bounds instead of the world bounds.
-      var filterRange = this.model_.bounds;
+      var rangeOfInterest;
+      if (this.rangeOfInterest_.isEmpty)
+        rangeOfInterest = this.model_.bounds;
+      else
+        rangeOfInterest = this.rangeOfInterest_;
 
+      var allGroup = new ResultsForGroup(this.model_, 'all');
       var resultsByGroupName = {};
       this.model_.getAllThreads().forEach(function(thread) {
         var groupName = this.getGroupNameForThread_(thread);
@@ -199,44 +293,86 @@ tvcm.exportTo('tracing', function() {
           resultsByGroupName[groupName] = new ResultsForGroup(
               this.model_, groupName);
         }
-        resultsByGroupName[groupName].appendThreadSlices(filterRange, thread);
-      }, this);
+        resultsByGroupName[groupName].appendThreadSlices(
+            rangeOfInterest, thread);
 
-      // Build chart data.
-      var groupNames = tvcm.dictionaryKeys(resultsByGroupName);
-      groupNames.sort();
+        allGroup.appendThreadSlices(rangeOfInterest, thread);
+      }, this);
 
-      var data = [];
-      groupNames.forEach(function(groupName) {
-        var resultsForGroup = resultsByGroupName[groupName];
-        var value;
+      // Helper function for working with the produced group.
+      var getValueFromGroup = function(group) {
         if (this.groupingUnit_ == WALL_TIME_GROUPING_UNIT)
-          value = resultsForGroup.wallTime;
-        else
-          value = resultsForGroup.cpuTime;
-        if (value === 0)
-          return;
-        data.push({
-          label: groupName,
-          value: value
-        });
-      }, this);
-      if (data.length == 0) {
+          return group.wallTime;
+        return group.cpuTime;
+      }.bind(this);
+
+      // Create summary.
+      var summaryText = document.createElement('div');
+      summaryText.appendChild(tvcm.ui.createSpan({
+        textContent: 'Total ' + this.groupingUnit_ + ': ',
+        bold: true}));
+      summaryText.appendChild(tvcm.ui.createSpan({
+        textContent: tsRound(getValueFromGroup(allGroup)) + 'ms'
+      }));
+      resultArea.appendChild(summaryText);
+
+      // Create the actual chart.
+      var otherGroup = new ResultsForGroup(this.model_, 'Other');
+      var groups = trimPieChartData(
+          tvcm.dictionaryValues(resultsByGroupName),
+          otherGroup,
+          getValueFromGroup);
+      if (groups.length == 0) {
         resultArea.appendChild(tvcm.ui.createSpan({textContent: 'No data'}));
+        return undefined;
+      }
+
+      this.chart_ = createPieChartFromResultGroups(
+          groups,
+          this.groupingUnit_ + ' breakdown by ' + this.groupBy_,
+          getValueFromGroup);
+      resultArea.appendChild(this.chart_);
+      this.chart_.addEventListener('click', function() {
+        var event = new tracing.RequestSelectionChangeEvent();
+        event.selection = new tracing.Selection([]);
+        this.dispatchEvent(event);
+      });
+      this.chart_.setSize(this.chart_.getMinSize());
+    },
+
+    get selection() {
+      return selection_;
+    },
+
+    set selection(selection) {
+      this.selection_ = selection;
+
+      if (this.chart_ === undefined)
         return;
+
+      if (selection.timeSummaryGroupName) {
+        this.chart_.highlightedLegendKey =
+            selection.timeSummaryGroupName;
+      } else {
+        this.chart_.highlightedLegendKey = undefined;
       }
-      var chart = new tvcm.ui.PieChart();
-      chart.width = 400;
-      chart.height = 400;
-      chart.chartTitle = this.groupingUnit_ + ' breakdown by ' + this.groupBy_;
-      chart.data = data;
-      resultArea.appendChild(chart);
+    },
+
+    get rangeOfInterest() {
+      return this.rangeOfInterest_;
+    },
+
+    set rangeOfInterest(rangeOfInterest) {
+      this.rangeOfInterest_ = rangeOfInterest;
+      this.updateContents_();
     }
   };
 
   tracing.TimelineViewSidePanel.registerPanelSubtype(TimeSummarySidePanel);
 
   return {
+    trimPieChartData: trimPieChartData,
+    createPieChartFromResultGroups: createPieChartFromResultGroups,
     ResultsForGroup: ResultsForGroup,
     TimeSummarySidePanel: TimeSummarySidePanel
   };