Title: [133915] trunk/Source/WebCore
Revision
133915
Author
ca...@chromium.org
Date
2012-11-08 10:24:07 -0800 (Thu, 08 Nov 2012)

Log Message

Web Inspector: show statistics over selected frame range in Timeline's Frame mode
https://bugs.webkit.org/show_bug.cgi?id=101593

Reviewed by Pavel Feldman.

- change status bar records counter wording to "N of M frames|records shown" depending on mode;
- append average frame length and & stddev in frame mode;
- expand the above to a popover that includes frame count, range duration and min/avg/max/stddev on frame length;
- show frame bars & dividers iff selection range includes < 30 frames (drive-by)

* English.lproj/localizedStrings.js:
* inspector/front-end/TimelineFrameController.js:
(WebInspector.FrameStatistics):
* inspector/front-end/TimelineModel.js:
(WebInspector.TimelineModel.aggregateTimeByCategory):
* inspector/front-end/TimelinePanel.js:
(WebInspector.TimelinePanel.prototype.get statusBarItems):
(WebInspector.TimelinePanel.prototype._createStatusBarItems.getAnchor):
(WebInspector.TimelinePanel.prototype._createStatusBarItems):
(WebInspector.TimelinePanel.prototype._updateRecordsCounter):
(WebInspector.TimelinePanel.prototype._updateFrameStatistics):
(WebInspector.TimelinePanel.prototype._showFrameStatistics):
(WebInspector.TimelinePanel.prototype._updateFrameBars):
(WebInspector.TimelinePanel.prototype._overviewModeChanged.set if):
(WebInspector.TimelinePanel.prototype._overviewModeChanged):
(WebInspector.TimelinePanel.prototype._refresh):
* inspector/front-end/TimelinePresentationModel.js:
(WebInspector.TimelinePresentationModel.prototype.):
(WebInspector.TimelinePresentationModel.prototype.compareEndTime):
(WebInspector.TimelinePresentationModel.prototype.filteredFrames):
(WebInspector.TimelinePresentationModel.generatePopupContentForFrameStatistics):
* inspector/front-end/timelinePanel.css:
(.timeline-records-stats, .storage-application-cache-status, .storage-application-cache-connectivity):
(.timeline-records-stats):
(.timeline-frames-stats):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (133914 => 133915)


--- trunk/Source/WebCore/ChangeLog	2012-11-08 18:21:23 UTC (rev 133914)
+++ trunk/Source/WebCore/ChangeLog	2012-11-08 18:24:07 UTC (rev 133915)
@@ -1,3 +1,41 @@
+2012-11-08  Andrey Kosyakov  <ca...@chromium.org>
+
+        Web Inspector: show statistics over selected frame range in Timeline's Frame mode
+        https://bugs.webkit.org/show_bug.cgi?id=101593
+
+        Reviewed by Pavel Feldman.
+
+        - change status bar records counter wording to "N of M frames|records shown" depending on mode;
+        - append average frame length and & stddev in frame mode;
+        - expand the above to a popover that includes frame count, range duration and min/avg/max/stddev on frame length;
+        - show frame bars & dividers iff selection range includes < 30 frames (drive-by)
+
+        * English.lproj/localizedStrings.js:
+        * inspector/front-end/TimelineFrameController.js:
+        (WebInspector.FrameStatistics):
+        * inspector/front-end/TimelineModel.js:
+        (WebInspector.TimelineModel.aggregateTimeByCategory):
+        * inspector/front-end/TimelinePanel.js:
+        (WebInspector.TimelinePanel.prototype.get statusBarItems):
+        (WebInspector.TimelinePanel.prototype._createStatusBarItems.getAnchor):
+        (WebInspector.TimelinePanel.prototype._createStatusBarItems):
+        (WebInspector.TimelinePanel.prototype._updateRecordsCounter):
+        (WebInspector.TimelinePanel.prototype._updateFrameStatistics):
+        (WebInspector.TimelinePanel.prototype._showFrameStatistics):
+        (WebInspector.TimelinePanel.prototype._updateFrameBars):
+        (WebInspector.TimelinePanel.prototype._overviewModeChanged.set if):
+        (WebInspector.TimelinePanel.prototype._overviewModeChanged):
+        (WebInspector.TimelinePanel.prototype._refresh):
+        * inspector/front-end/TimelinePresentationModel.js:
+        (WebInspector.TimelinePresentationModel.prototype.):
+        (WebInspector.TimelinePresentationModel.prototype.compareEndTime):
+        (WebInspector.TimelinePresentationModel.prototype.filteredFrames):
+        (WebInspector.TimelinePresentationModel.generatePopupContentForFrameStatistics):
+        * inspector/front-end/timelinePanel.css:
+        (.timeline-records-stats, .storage-application-cache-status, .storage-application-cache-connectivity):
+        (.timeline-records-stats):
+        (.timeline-frames-stats):
+
 2012-11-08  Jakob Petsovits  <jpetsov...@rim.com>
 
         [BlackBerry] Rework the API to use document coordinates

Modified: trunk/Source/WebCore/English.lproj/localizedStrings.js (133914 => 133915)


--- trunk/Source/WebCore/English.lproj/localizedStrings.js	2012-11-08 18:21:23 UTC (rev 133914)
+++ trunk/Source/WebCore/English.lproj/localizedStrings.js	2012-11-08 18:24:07 UTC (rev 133915)
@@ -25,7 +25,9 @@
 localizedStrings["%d errors, %d warning"] = "%d errors, %d warning";
 localizedStrings["%d errors, %d warnings"] = "%d errors, %d warnings";
 localizedStrings["%d of %d"] = "%d of %d";
-localizedStrings["%d of %d captured records are visible"] = "%d of %d captured records are visible";
+localizedStrings["%d of %d records shown"] = "%d of %d records shown";
+localizedStrings["%d of %d frames shown"] = "%d of %d frames shown";
+localizedStrings["avg: %s, \u03c3: %s"] = "avg: %s, \u03c3: %s";
 localizedStrings["%d warning"] = "%d warning";
 localizedStrings["%d warnings"] = "%d warnings";
 localizedStrings["%d \xd7 %d pixels (Natural: %d \xd7 %d pixels)"] = "%d \xd7 %d pixels (Natural: %d \xd7 %d pixels)";
@@ -797,3 +799,11 @@
 localizedStrings["Detached from the target"] = "Detached from the target";
 localizedStrings["Remote debugging has been terminated with reason: "] = "Remote debugging has been terminated with reason: ";
 localizedStrings["Please re-attach to the new target."] = "Please re-attach to the new target.";
+localizedStrings["%s (%.0f FPS)"] = "%s (%.0f FPS)";
+localizedStrings["Selected Range"] = "Selected Range";
+localizedStrings["%s\u2013%s (%d frames)"] = "%s\u2013%s (%d frames)";
+localizedStrings["Minimum Time"] = "Minimum Time";
+localizedStrings["Average Time"] = "Average Time";
+localizedStrings["Maximum Time"] = "Maximum Time";
+localizedStrings["Standard Deviation"] = "Standard Deviation";
+localizedStrings["Time by category"] = "Time by category";

Modified: trunk/Source/WebCore/inspector/front-end/TimelineFrameController.js (133914 => 133915)


--- trunk/Source/WebCore/inspector/front-end/TimelineFrameController.js	2012-11-08 18:21:23 UTC (rev 133914)
+++ trunk/Source/WebCore/inspector/front-end/TimelineFrameController.js	2012-11-08 18:24:07 UTC (rev 133915)
@@ -107,7 +107,36 @@
 
 /**
  * @constructor
+ * @param {Array.<WebInspector.TimelineFrame>} frames
  */
+WebInspector.FrameStatistics = function(frames)
+{
+    this.frameCount = frames.length;
+    this.minDuration = Infinity;
+    this.maxDuration = 0;
+    this.timeByCategory = {};
+    this.startOffset = frames[0].startTimeOffset;
+    var lastFrame = frames[this.frameCount - 1];
+    this.endOffset = lastFrame.startTimeOffset + lastFrame.duration;
+
+    var totalDuration = 0;
+    var sumOfSquares = 0;
+    for (var i = 0; i < this.frameCount; ++i) {
+        var duration = frames[i].duration;
+        totalDuration += duration;
+        sumOfSquares += duration * duration;
+        this.minDuration = Math.min(this.minDuration, duration);
+        this.maxDuration = Math.max(this.maxDuration, duration);
+        WebInspector.TimelineModel.aggregateTimeByCategory(this.timeByCategory, frames[i].timeByCategory);
+    }
+    this.average = totalDuration / this.frameCount;
+    var variance = sumOfSquares / this.frameCount - this.average * this.average;
+    this.stddev = Math.sqrt(variance);
+}
+
+/**
+ * @constructor
+ */
 WebInspector.TimelineFrame = function()
 {
     this.timeByCategory = {};

Modified: trunk/Source/WebCore/inspector/front-end/TimelineModel.js (133914 => 133915)


--- trunk/Source/WebCore/inspector/front-end/TimelineModel.js	2012-11-08 18:21:23 UTC (rev 133914)
+++ trunk/Source/WebCore/inspector/front-end/TimelineModel.js	2012-11-08 18:24:07 UTC (rev 133915)
@@ -129,6 +129,16 @@
     total[categoryName] = (total[categoryName] || 0) + ownTime;
 }
 
+/**
+ * @param {Object} total
+ * @param {Object} addend
+ */
+WebInspector.TimelineModel.aggregateTimeByCategory = function(total, addend)
+{
+    for (var category in addend)
+        total[category] = (total[category] || 0) + addend[category];
+}
+
 WebInspector.TimelineModel.prototype = {
     startRecord: function()
     {

Modified: trunk/Source/WebCore/inspector/front-end/TimelinePanel.js (133914 => 133915)


--- trunk/Source/WebCore/inspector/front-end/TimelinePanel.js	2012-11-08 18:21:23 UTC (rev 133914)
+++ trunk/Source/WebCore/inspector/front-end/TimelinePanel.js	2012-11-08 18:24:07 UTC (rev 133915)
@@ -206,7 +206,8 @@
     {
         return this._statusBarButtons.select("element").concat([
             this._miscStatusBarItems,
-            this.recordsCounter
+            this.recordsCounter,
+            this.frameStatistics
         ]);
     },
 
@@ -254,7 +255,15 @@
         }
 
         this.recordsCounter = document.createElement("span");
-        this.recordsCounter.className = "timeline-records-counter";
+        this.recordsCounter.className = "timeline-records-stats";
+
+        this.frameStatistics = document.createElement("span");
+        this.frameStatistics.className = "timeline-records-stats hidden";
+        function getAnchor()
+        {
+            return this.frameStatistics;
+        }
+        this._frameStatisticsPopoverHelper = new WebInspector.PopoverHelper(this.frameStatistics, getAnchor.bind(this), this._showFrameStatistics.bind(this));
     },
 
     _createTimelineCategoryStatusBarCheckbox: function(category, onCheckboxClicked)
@@ -392,9 +401,34 @@
 
     _updateRecordsCounter: function(recordsInWindowCount)
     {
-        this.recordsCounter.textContent = WebInspector.UIString("%d of %d captured records are visible", recordsInWindowCount, this._allRecordsCount);
+        this.recordsCounter.textContent = WebInspector.UIString("%d of %d records shown", recordsInWindowCount, this._allRecordsCount);
     },
 
+    _updateFrameStatistics: function(frames)
+    {
+        if (frames.length) {
+            this._lastFrameStatistics = new WebInspector.FrameStatistics(frames);
+            var details = WebInspector.UIString("avg: %s, \u03c3: %s",
+                Number.secondsToString(this._lastFrameStatistics.average, true), Number.secondsToString(this._lastFrameStatistics.stddev, true));
+        } else
+            this._lastFrameStatistics = null;
+        this.frameStatistics.textContent = WebInspector.UIString("%d of %d frames shown", frames.length, this._presentationModel.frames().length);
+        if (details) {
+            this.frameStatistics.appendChild(document.createTextNode(" ("));
+            this.frameStatistics.createChild("span", "timeline-frames-stats").textContent = details;
+            this.frameStatistics.appendChild(document.createTextNode(")"));
+        }
+    },
+
+    /**
+     * @param {Element} anchor
+     * @param {WebInspector.Popover} popover
+     */
+    _showFrameStatistics: function(anchor, popover)
+    {
+        popover.show(WebInspector.TimelinePresentationModel.generatePopupContentForFrameStatistics(this._lastFrameStatistics), anchor);
+    },
+
     _updateEventDividers: function()
     {
         this._timelineGrid.removeEventDividers();
@@ -414,14 +448,8 @@
         this._timelineGrid.addEventDividers(dividers);
     },
 
-    _shouldShowFrames: function()
+    _updateFrameBars: function(frames)
     {
-        return this._frameMode && this._presentationModel.frames().length > 0 && this.calculator.boundarySpan() < 1.0;
-    },
-
-    _updateFrames: function()
-    {
-        var frames = this._presentationModel.frames();
         var clientWidth = this._graphRowsElementWidth;
         if (this._frameContainer)
             this._frameContainer.removeChildren();
@@ -440,8 +468,6 @@
             var frame = frames[i];
             var frameStart = this._calculator.computePosition(frame.startTime);
             var frameEnd = this._calculator.computePosition(frame.endTime);
-            if (frameEnd <= 0 || frameStart >= clientWidth)
-                continue;
 
             var frameStrip = document.createElement("div");
             frameStrip.className = "timeline-frame-strip";
@@ -488,11 +514,15 @@
 
             if (frameMode) {
                 this.element.addStyleClass("timeline-frame-overview");
+                this.recordsCounter.addStyleClass("hidden");
+                this.frameStatistics.removeStyleClass("hidden");
                 this._frameController = new WebInspector.TimelineFrameController(this._model, this._overviewPane, this._presentationModel);
             } else {
                 this._frameController.dispose();
                 this._frameController = null;
                 this.element.removeStyleClass("timeline-frame-overview");
+                this.recordsCounter.removeStyleClass("hidden");
+                this.frameStatistics.addStyleClass("hidden");
             }
         }
         if (shouldShowMemory === this._memoryStatistics.visible())
@@ -707,14 +737,19 @@
 
         var recordsInWindowCount = this._refreshRecords();
         this._updateRecordsCounter(recordsInWindowCount);
-        if(!this._boundariesAreValid) {
+        if (!this._boundariesAreValid) {
             this._updateEventDividers();
-            if (this._shouldShowFrames()) {
-                this._timelineGrid.removeDividers();
-                this._updateFrames();
-            } else {
+            var frames = this._frameController && this._presentationModel.filteredFrames(this._overviewPane.windowStartTime(), this._overviewPane.windowEndTime());
+            if (frames) {
+                this._updateFrameStatistics(frames);
+                const maxFramesForFrameBars = 30;
+                if  (frames.length && frames.length < maxFramesForFrameBars) {
+                    this._timelineGrid.removeDividers();
+                    this._updateFrameBars(frames);
+                } else
+                    this._timelineGrid.updateDividers(this._calculator);
+            } else
                 this._timelineGrid.updateDividers(this._calculator);
-            }
             if (this._mainThreadMonitoringEnabled)
                 this._refreshMainThreadBars();
         }

Modified: trunk/Source/WebCore/inspector/front-end/TimelinePresentationModel.js (133914 => 133915)


--- trunk/Source/WebCore/inspector/front-end/TimelinePresentationModel.js	2012-11-08 18:21:23 UTC (rev 133914)
+++ trunk/Source/WebCore/inspector/front-end/TimelinePresentationModel.js	2012-11-08 18:24:07 UTC (rev 133915)
@@ -434,6 +434,23 @@
         return recordsInWindow;
     },
 
+    filteredFrames: function(startTime, endTime)
+    {
+        function compareStartTime(value, object)
+        {
+            return value - object.startTime;
+        }
+        function compareEndTime(value, object)
+        {
+            return value - object.endTime;
+        }
+        var firstFrame = insertionIndexForObjectInListSortedByFunction(startTime, this._frames, compareStartTime);
+        var lastFrame = insertionIndexForObjectInListSortedByFunction(endTime, this._frames, compareEndTime);
+        while (lastFrame < this._frames.length && this._frames[lastFrame].endTime <= endTime)
+            ++lastFrame;
+        return this._frames.slice(firstFrame, lastFrame);
+    },
+
     isVisible: function(record)
     {
         for (var i = 0; i < this._filters.length; ++i) {
@@ -1127,6 +1144,33 @@
 }
 
 /**
+ * @param {WebInspector.FrameStatistics} statistics
+ */
+WebInspector.TimelinePresentationModel.generatePopupContentForFrameStatistics = function(statistics)
+{
+    /**
+     * @param {number} time
+     */
+    function formatTimeAndFPS(time)
+    {
+        return WebInspector.UIString("%s (%.0f FPS)", Number.secondsToString(time, true), 1 / time);
+    }
+
+    var contentHelper = new WebInspector.TimelinePresentationModel.PopupContentHelper(WebInspector.UIString("Selected Range"));
+
+    contentHelper._appendTextRow(WebInspector.UIString("Selected range"), WebInspector.UIString("%s\u2013%s (%d frames)",
+        Number.secondsToString(statistics.startOffset, true), Number.secondsToString(statistics.endOffset, true), statistics.frameCount));
+    contentHelper._appendTextRow(WebInspector.UIString("Minimum Time"), formatTimeAndFPS(statistics.minDuration));
+    contentHelper._appendTextRow(WebInspector.UIString("Average Time"), formatTimeAndFPS(statistics.average));
+    contentHelper._appendTextRow(WebInspector.UIString("Maximum Time"), formatTimeAndFPS(statistics.maxDuration));
+    contentHelper._appendTextRow(WebInspector.UIString("Standard Deviation"), Number.secondsToString(statistics.stddev, true));
+    contentHelper._appendElementRow(WebInspector.UIString("Time by category"),
+        WebInspector.TimelinePresentationModel._generateAggregatedInfo(statistics.timeByCategory));
+
+    return contentHelper._contentTable;
+}
+
+/**
  * @param {CanvasRenderingContext2D} context
  * @param {number} width
  * @param {number} height

Modified: trunk/Source/WebCore/inspector/front-end/timelinePanel.css (133914 => 133915)


--- trunk/Source/WebCore/inspector/front-end/timelinePanel.css	2012-11-08 18:21:23 UTC (rev 133914)
+++ trunk/Source/WebCore/inspector/front-end/timelinePanel.css	2012-11-08 18:24:07 UTC (rev 133915)
@@ -429,17 +429,23 @@
     background-color: rgb(66, 129, 235) !important;
 }
 
-.timeline-records-counter, .storage-application-cache-status, .storage-application-cache-connectivity {
+.timeline-records-stats, .storage-application-cache-status, .storage-application-cache-connectivity {
     font-size: 11px;
     text-shadow: white 0 1px 0;
 }
 
-.timeline-records-counter {
+.timeline-records-stats {
     float: right;
     margin-top: 5px;
     margin-right: 6px;
 }
 
+.timeline-frames-stats {
+    pointer-events: auto;
+    text-decoration: underline;
+    cursor: pointer;
+}
+
 #resources-container-content {
     overflow: visible;
     min-height: 100%;
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to