Diff
Modified: trunk/LayoutTests/ChangeLog (113993 => 113994)
--- trunk/LayoutTests/ChangeLog 2012-04-12 16:20:48 UTC (rev 113993)
+++ trunk/LayoutTests/ChangeLog 2012-04-12 16:29:38 UTC (rev 113994)
@@ -1,3 +1,13 @@
+2012-04-11 Andrey Kosyakov <[email protected]>
+
+ Web Inspector: show wall time frame duration on the vertical overview
+ https://bugs.webkit.org/show_bug.cgi?id=83718
+
+ Reviewed by Pavel Feldman.
+
+ * inspector/utilities-expected.txt:
+ * inspector/utilities.html: added results for Array.prototype.qselect()
+
2012-04-12 Pierre Rossi <[email protected]>
[Qt] More unreviewed gardening after r113968.
Modified: trunk/LayoutTests/inspector/utilities-expected.txt (113993 => 113994)
--- trunk/LayoutTests/inspector/utilities-expected.txt 2012-04-12 16:20:48 UTC (rev 113993)
+++ trunk/LayoutTests/inspector/utilities-expected.txt 2012-04-12 16:29:38 UTC (rev 113994)
@@ -3,5 +3,25 @@
Running: binaryIndexOfTest
+Running: qselectTest
+Array: []
+Reference: {}
+Actual: {}
+Array: [0]
+Reference: {"min":0,"median":0,"max":0}
+Actual: {"min":0,"median":0,"max":0}
+Array: [0,0,0,0,0,0,0,0]
+Reference: {"min":0,"median":0,"max":0}
+Actual: {"min":0,"median":0,"max":0}
+Array: [4,3,2,1]
+Reference: {"min":1,"median":3,"max":4}
+Actual: {"min":1,"median":3,"max":4}
+Array: [1,2,3,4,5]
+Reference: {"min":1,"median":3,"max":5}
+Actual: {"min":1,"median":3,"max":5}
+Array: [-1,3,2,7,7,7,10,12,3,4,-1,2]
+Reference: {"min":-1,"median":4,"max":12}
+Actual: {"min":-1,"median":4,"max":12}
+
Running: sortRangeTest
Modified: trunk/LayoutTests/inspector/utilities.html (113993 => 113994)
--- trunk/LayoutTests/inspector/utilities.html 2012-04-12 16:20:48 UTC (rev 113993)
+++ trunk/LayoutTests/inspector/utilities.html 2012-04-12 16:29:38 UTC (rev 113994)
@@ -37,6 +37,45 @@
next();
},
+ function qselectTest(next)
+ {
+ var testArrays = [
+ [],
+ [0],
+ [0, 0, 0, 0, 0, 0, 0, 0],
+ [4, 3, 2, 1],
+ [1, 2, 3, 4, 5],
+ [-1, 3, 2, 7, 7, 7, 10, 12, 3, 4, -1, 2]
+ ];
+
+ function testArray(array)
+ {
+ function compare(a, b)
+ {
+ return a - b;
+ }
+ var sorted = array.slice(0).sort(compare);
+
+ var reference = {
+ min: sorted[0],
+ median: sorted[Math.floor(sorted.length / 2)],
+ max: sorted[sorted.length - 1]
+ }
+
+ var actual = {
+ min: array.slice(0).qselect(0),
+ median: array.slice(0).qselect(Math.floor(array.length / 2)),
+ max: array.slice(0).qselect(array.length - 1)
+ }
+ InspectorTest.addResult("Array: " + JSON.stringify(array));
+ InspectorTest.addResult("Reference: " + JSON.stringify(reference));
+ InspectorTest.addResult("Actual: " + JSON.stringify(actual));
+ }
+ for (var i = 0, l = testArrays.length; i < l; ++i)
+ testArray(testArrays[i]);
+ next();
+ },
+
function sortRangeTest(next)
{
var testArrays = [
Modified: trunk/Source/WebCore/ChangeLog (113993 => 113994)
--- trunk/Source/WebCore/ChangeLog 2012-04-12 16:20:48 UTC (rev 113993)
+++ trunk/Source/WebCore/ChangeLog 2012-04-12 16:29:38 UTC (rev 113994)
@@ -1,3 +1,34 @@
+2012-04-11 Andrey Kosyakov <[email protected]>
+
+ Web Inspector: show wall time frame duration on the vertical overview
+ https://bugs.webkit.org/show_bug.cgi?id=83718
+
+ Reviewed by Pavel Feldman.
+
+ - show frame duration as a contour of the frame bar in vertical overview;
+ - when scaling, normalize bar to a max of 3 * median frame duration, so too long frames do not dwarf the majority;
+ - add Array.prototype.qselect() for the above;
+ - factor our partition algorithm onto Array.prototype for better reuse;
+
+ * inspector/front-end/PartialQuickSort.js: factor out partition()
+ (Object.defineProperty.):
+ (Object.defineProperty):
+ * inspector/front-end/TimelineFrameController.js:
+ (WebInspector.TimelineFrameController.prototype._flushFrame): add frame.duration.
+ (WebInspector.TimelineFrameController.prototype._createSyntheticFrame): ditto.
+ * inspector/front-end/TimelineOverviewPane.js:
+ (WebInspector.TimelineVerticalOverview):
+ (WebInspector.TimelineVerticalOverview.prototype.reset): this._longestFrameTime is gone.
+ (WebInspector.TimelineVerticalOverview.prototype.update): ditto.
+ (WebInspector.TimelineVerticalOverview.prototype._aggregateFrames): return frames to display, not just their timeByCatogory.
+ (WebInspector.TimelineVerticalOverview.prototype._renderBars): use entire frames, not timeByCategory.
+ (WebInspector.TimelineVerticalOverview.prototype._renderBar): ditto.
+ * inspector/front-end/externs.js: Added annotations for partition() and qselect()
+ (Array.prototype.partition):
+ (Array.prototype.qselect):
+ * inspector/front-end/utilities.js:
+ (.):
+
2012-04-12 David Barr <[email protected]>
Optimize non-rounded double border rendering to avoid transparency layers
Modified: trunk/Source/WebCore/inspector/front-end/PartialQuickSort.js (113993 => 113994)
--- trunk/Source/WebCore/inspector/front-end/PartialQuickSort.js 2012-04-12 16:20:48 UTC (rev 113993)
+++ trunk/Source/WebCore/inspector/front-end/PartialQuickSort.js 2012-04-12 16:29:38 UTC (rev 113994)
@@ -32,34 +32,12 @@
/** @this {Array} */
function(comparator, leftBound, rightBound, k)
{
- function swap(array, i1, i2)
- {
- var temp = array[i1];
- array[i1] = array[i2];
- array[i2] = temp;
- }
-
- function partition(array, comparator, left, right, pivotIndex)
- {
- var pivotValue = array[pivotIndex];
- swap(array, right, pivotIndex);
- var storeIndex = left;
- for (var i = left; i < right; ++i) {
- if (comparator(array[i], pivotValue) < 0) {
- swap(array, storeIndex, i);
- ++storeIndex;
- }
- }
- swap(array, right, storeIndex);
- return storeIndex;
- }
-
function quickSortFirstK(array, comparator, left, right, k)
{
if (right <= left)
return;
var pivotIndex = Math.floor(Math.random() * (right - left)) + left;
- var pivotNewIndex = partition(array, comparator, left, right, pivotIndex);
+ var pivotNewIndex = array.partition(comparator, left, right, pivotIndex);
quickSortFirstK(array, comparator, left, pivotNewIndex - 1, k);
if (pivotNewIndex < left + k - 1)
quickSortFirstK(array, comparator, pivotNewIndex + 1, right, k);
Modified: trunk/Source/WebCore/inspector/front-end/TimelineFrameController.js (113993 => 113994)
--- trunk/Source/WebCore/inspector/front-end/TimelineFrameController.js 2012-04-12 16:20:48 UTC (rev 113993)
+++ trunk/Source/WebCore/inspector/front-end/TimelineFrameController.js 2012-04-12 16:29:38 UTC (rev 113994)
@@ -78,6 +78,7 @@
var frameBeginTime = WebInspector.TimelineModel.startTimeInSeconds(record);
if (this._lastFrame) {
this._lastFrame.endTime = frameBeginTime;
+ this._lastFrame.duration = this._lastFrame.endTime - this._lastFrame.startTime;
this._overviewPane.addFrame(this._lastFrame);
this._presentationModel.addFrame(this._lastFrame);
}
@@ -92,7 +93,8 @@
frame.startTime = WebInspector.TimelineModel.startTimeInSeconds(record);
frame.startTimeOffset = this._model.recordOffsetInSeconds(record);
frame.endTime = WebInspector.TimelineModel.endTimeInSeconds(record);
- frame.cpuTime = WebInspector.TimelineModel.durationInSeconds(record);
+ frame.duration = WebInspector.TimelineModel.durationInSeconds(record);
+ frame.cpuTime = frame.duration;
return frame;
},
Modified: trunk/Source/WebCore/inspector/front-end/TimelineOverviewPane.js (113993 => 113994)
--- trunk/Source/WebCore/inspector/front-end/TimelineOverviewPane.js 2012-04-12 16:20:48 UTC (rev 113993)
+++ trunk/Source/WebCore/inspector/front-end/TimelineOverviewPane.js 2012-04-12 16:29:38 UTC (rev 113994)
@@ -909,6 +909,7 @@
this._borderStyles.loading = "rgb(106, 152, 213)";
this._borderStyles.scripting = "rgb(223, 175, 77)";
this._borderStyles.rendering = "rgb(130, 59, 190)";
+ this._borderStyles._frameLength = "rgb(90, 90, 90)";
}
WebInspector.TimelineVerticalOverview.prototype = {
@@ -916,7 +917,6 @@
{
this._recordsPerBar = 1;
this._barTimes = [];
- this._longestBarTime = 0;
this._frames = [];
},
@@ -925,13 +925,12 @@
const minBarWidth = 4;
var framesPerBar = Math.max(1, this._frames.length * minBarWidth / this.element.clientWidth);
this._barTimes = [];
- this._longestBarTime = 0;
- var barHeights = this._aggregateFrames(framesPerBar);
+ var visibleFrames = this._aggregateFrames(framesPerBar);
const paddingTop = 4;
- var scale = (this.element.clientHeight - paddingTop) / this._longestBarTime;
+ var scale = (this.element.clientHeight - paddingTop) / this._normalBarLength;
- this._renderBars(barHeights, scale);
+ this._renderBars(visibleFrames, scale);
},
/**
@@ -947,64 +946,89 @@
*/
_aggregateFrames: function(framesPerBar)
{
- var statistics = [];
+ var visibleFrames = [];
+ var durations = [];
+ var longestFrameTime = 0;
+
for (var barNumber = 0, currentFrame = 0; currentFrame < this._frames.length; ++barNumber) {
var barStartTime = this._frames[currentFrame].startTime;
var longestFrame = null;
for (var lastFrame = Math.min(Math.floor((barNumber + 1) * framesPerBar), this._frames.length);
currentFrame < lastFrame; ++currentFrame) {
- if (!longestFrame || longestFrame.cpuTime < this._frames[currentFrame].cpuTime)
+ if (!longestFrame || longestFrame.duration < this._frames[currentFrame].duration)
longestFrame = this._frames[currentFrame];
}
var barEndTime = this._frames[currentFrame - 1].endTime;
if (longestFrame) {
- this._longestBarTime = Math.max(this._longestBarTime, longestFrame.cpuTime);
- statistics.push(longestFrame.timeByCategory);
+ longestFrameTime = Math.max(longestFrameTime, longestFrame.duration);
+ visibleFrames.push(longestFrame);
this._barTimes.push({ startTime: barStartTime, endTime: barEndTime });
+ durations.push(longestFrame.duration);
}
}
- return statistics;
+ // Do not let occasional very long frames to dwarf the majority -- use at most 3 * median frame length for scale.
+ this._normalBarLength = Math.min(longestFrameTime, 3 * durations.qselect(Math.floor(durations.length / 2)));
+ return visibleFrames;
},
- _renderBars: function(allBarHeights, scale)
+ _renderBars: function(frames, scale)
{
// Use real world, 1:1 coordinates in canvas. This will also take care of clearing it.
this.element.width = this.element.clientWidth;
this.element.height = this.element.clientHeight;
const maxPadding = 5;
- this._actualOuterBarWidth = Math.min((this.element.width - 2 * this._outerPadding) / allBarHeights.length, this._maxInnerBarWidth + maxPadding);
+ this._actualOuterBarWidth = Math.min((this.element.width - 2 * this._outerPadding) / frames.length, this._maxInnerBarWidth + maxPadding);
this._actualPadding = Math.min(Math.floor(this._actualOuterBarWidth / 3), maxPadding);
- for (var i = 0; i < allBarHeights.length; ++i)
- this._renderBar(this._outerPadding + this._actualOuterBarWidth * i, this._actualOuterBarWidth - this._actualPadding, allBarHeights[i], scale);
+ for (var i = 0; i < frames.length; ++i) {
+ var x = this._outerPadding + this._actualOuterBarWidth * i;
+ var width = this._actualOuterBarWidth - this._actualPadding;
+ this._renderBar(x, width, frames[i], scale);
+ }
},
- _renderBar: function(left, width, barHeights, scale)
+ _renderBar: function(left, width, frame, scale)
{
- var categories = Object.keys(barHeights);
+ var categories = Object.keys(frame.timeByCategory);
if (!categories.length)
return;
+ var x = Math.floor(left) + 0.5;
+ width = Math.floor(width);
+
for (var i = 0, bottomOffset = this.element.height; i < categories.length; ++i) {
var category = categories[i];
- var duration = barHeights[category];
+ var duration = frame.timeByCategory[category];
if (!duration)
continue;
var height = duration * scale;
+ var y = Math.floor(bottomOffset - height) + 0.5;
this._context.save();
- this._context.translate(Math.floor(left) + 0.5, 0);
+ this._context.translate(x, 0);
this._context.scale(width / this._maxInnerBarWidth, 1);
this._context.fillStyle = this._fillStyles[category];
- this._context.fillRect(0, bottomOffset - height, this._maxInnerBarWidth, height);
+ this._context.fillRect(0, y, this._maxInnerBarWidth, Math.floor(height));
this._context.restore();
this._context.strokeStyle = this._borderStyles[category];
- this._context.strokeRect(Math.floor(left) + 0.5, Math.floor(bottomOffset - height) + 0.5, Math.floor(width), Math.floor(height));
+ this._context.strokeRect(x, y, width, Math.floor(height));
bottomOffset -= height - 1;
}
+ // Draw a contour for the rest of frame time that we did not instrument.
+ var nonCPUTime = frame.duration - frame.cpuTime;
+ var y0 = Math.floor(bottomOffset - nonCPUTime * scale) + 0.5;
+ var y1 = Math.floor(bottomOffset) + 0.5;
+
+ this._context.strokeStyle = this._borderStyles._frameLength;
+ this._context.beginPath();
+ this._context.moveTo(x, y1);
+ this._context.lineTo(x, y0);
+ this._context.lineTo(x + width, y0);
+ this._context.lineTo(x + width, y1);
+ this._context.stroke();
},
getWindowTimes: function(windowLeft, windowRight)
Modified: trunk/Source/WebCore/inspector/front-end/externs.js (113993 => 113994)
--- trunk/Source/WebCore/inspector/front-end/externs.js 2012-04-12 16:20:48 UTC (rev 113993)
+++ trunk/Source/WebCore/inspector/front-end/externs.js 2012-04-12 16:29:38 UTC (rev 113994)
@@ -78,7 +78,24 @@
Array.prototype.binaryIndexOf = function(anchor) {}
Array.prototype.sortRange = function(comparator, leftBound, rightBound, k) {}
+/**
+ * @this {Array.<number>}
+ * @param {function(number,number):boolean} comparator
+ * @param {number} left
+ * @param {number} right
+ * @param {number} pivotIndex
+ * @return {number}
+ */
+Array.prototype.partition = function(comparator, left, right, pivotIndex) {}
+/**
+ * @this {Array.<number>}
+ * @param {number} k
+ * @param {function(number,number):boolean=} comparator
+ * @return {number}
+ */
+Array.prototype.qselect = function(k, comparator) {}
+
DOMApplicationCache.prototype.UNCACHED = 0;
DOMApplicationCache.prototype.IDLE = 1;
DOMApplicationCache.prototype.CHECKING = 2;
Modified: trunk/Source/WebCore/inspector/front-end/utilities.js (113993 => 113994)
--- trunk/Source/WebCore/inspector/front-end/utilities.js 2012-04-12 16:20:48 UTC (rev 113993)
+++ trunk/Source/WebCore/inspector/front-end/utilities.js 2012-04-12 16:29:38 UTC (rev 113994)
@@ -716,6 +716,66 @@
}
});
+Object.defineProperty(Array.prototype, "partition",
+{
+ /**
+ * @this {Array.<number>}
+ * @param {function(number,number):boolean} comparator
+ * @param {number} left
+ * @param {number} right
+ * @param {number} pivotIndex
+ */
+ value: function(comparator, left, right, pivotIndex)
+ {
+ function swap(array, i1, i2)
+ {
+ var temp = array[i1];
+ array[i1] = array[i2];
+ array[i2] = temp;
+ }
+
+ var pivotValue = this[pivotIndex];
+ swap(this, right, pivotIndex);
+ var storeIndex = left;
+ for (var i = left; i < right; ++i) {
+ if (comparator(this[i], pivotValue) < 0) {
+ swap(this, storeIndex, i);
+ ++storeIndex;
+ }
+ }
+ swap(this, right, storeIndex);
+ return storeIndex;
+ }
+});
+
+Object.defineProperty(Array.prototype, "qselect",
+{
+ /**
+ * @this {Array.<number>}
+ * @param {number} k
+ * @param {function(number,number):boolean=} comparator
+ */
+ value: function(k, comparator)
+ {
+ if (k < 0 || k >= this.length)
+ return;
+ if (!comparator)
+ comparator = function(a, b) { return a - b; }
+
+ var low = 0;
+ var high = this.length - 1;
+ for (;;) {
+ var pivotPosition = this.partition(comparator, low, high, Math.floor((high + low) / 2));
+ if (pivotPosition === k)
+ return this[k];
+ else if (pivotPosition > k)
+ high = pivotPosition - 1;
+ else
+ low = pivotPosition + 1;
+ }
+ }
+});
+
Array.diff = function(left, right)
{
var o = left;