<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[190676] trunk/Websites/perf.webkit.org</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/190676">190676</a></dd>
<dt>Author</dt> <dd>rniwa@webkit.org</dd>
<dt>Date</dt> <dd>2015-10-07 11:38:07 -0700 (Wed, 07 Oct 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Perf dashboard doesn't show analysis tasks anchored at outliers
https://bugs.webkit.org/show_bug.cgi?id=149870

Reviewed by Chris Dumez.

The bug was caused by the computation of start and end times of analysis tasks being dependent on
time series provided to the interactive chart component even though they are already filtered.

Since the interactive chart component shouldn't be messing with the underlying data models, moved
the code to compute start and end times to App.Pane, to where it belongs, and made the moved code use
the unfiltered time series newly exposed on ChartData.

Also fixed a bug in fetch-from-remote.php which resulted in Ember endlessly fetching same JSON files.

* public/admin/fetch-from-remote.php:
(.): Use the full request URI for HTTP requests and caching. Otherwise, we're going to mix up caches
and Ember can start hanging browsers (took me three hours to debug this).

* public/v2/app.js:
(App.Pane._showOutlierChanged): Added. Resets chartData when showOutlier flag has been changed.
(App.Pane.fetchAnalyticRanges): The old code wasn't filtering analysis tasks by platforms and metrics
at all since it relied on the server-side REST API to do the filtering, which I haven't implemented yet.
Filter the results manually instead.
(App.Pane.ranges): Moved the logic to compute startTime and endTime here from InteractiveChartComponent.
(App.PaneController.toggleShowOutlier): Now that App.Pane responds to showOutlier changes, we don't
need to call a private method on it.
(App.AnalysisTaskController._chartDataChanged): When end points are not found, try showing outliers.
This will cause chartData to be modified so just exit early and wait for getting called again.

* public/v2/interactive-chart.js:
(App.InteractiveChartComponent._rangesChanged): The code to compute start and end time has been moved
to App.Pane.ranges.

* public/v2/manifest.js:
(App.Manifest._formatFetchedData): Added unfiltered time series as new properties as they are now used
to compute the end points of analysis tasks when their end points are outliers.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkWebsitesperfwebkitorgChangeLog">trunk/Websites/perf.webkit.org/ChangeLog</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicadminfetchfromremotephp">trunk/Websites/perf.webkit.org/public/admin/fetch-from-remote.php</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv2appjs">trunk/Websites/perf.webkit.org/public/v2/app.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv2interactivechartjs">trunk/Websites/perf.webkit.org/public/v2/interactive-chart.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv2manifestjs">trunk/Websites/perf.webkit.org/public/v2/manifest.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkWebsitesperfwebkitorgChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/ChangeLog (190675 => 190676)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/ChangeLog        2015-10-07 18:26:19 UTC (rev 190675)
+++ trunk/Websites/perf.webkit.org/ChangeLog        2015-10-07 18:38:07 UTC (rev 190676)
</span><span class="lines">@@ -1,5 +1,44 @@
</span><span class="cx"> 2015-10-07  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
</span><span class="cx"> 
</span><ins>+        Perf dashboard doesn't show analysis tasks anchored at outliers
+        https://bugs.webkit.org/show_bug.cgi?id=149870
+
+        Reviewed by Chris Dumez.
+
+        The bug was caused by the computation of start and end times of analysis tasks being dependent on
+        time series provided to the interactive chart component even though they are already filtered.
+
+        Since the interactive chart component shouldn't be messing with the underlying data models, moved
+        the code to compute start and end times to App.Pane, to where it belongs, and made the moved code use
+        the unfiltered time series newly exposed on ChartData.
+
+        Also fixed a bug in fetch-from-remote.php which resulted in Ember endlessly fetching same JSON files.
+
+        * public/admin/fetch-from-remote.php:
+        (.): Use the full request URI for HTTP requests and caching. Otherwise, we're going to mix up caches
+        and Ember can start hanging browsers (took me three hours to debug this).
+
+        * public/v2/app.js:
+        (App.Pane._showOutlierChanged): Added. Resets chartData when showOutlier flag has been changed.
+        (App.Pane.fetchAnalyticRanges): The old code wasn't filtering analysis tasks by platforms and metrics
+        at all since it relied on the server-side REST API to do the filtering, which I haven't implemented yet.
+        Filter the results manually instead.
+        (App.Pane.ranges): Moved the logic to compute startTime and endTime here from InteractiveChartComponent.
+        (App.PaneController.toggleShowOutlier): Now that App.Pane responds to showOutlier changes, we don't
+        need to call a private method on it.
+        (App.AnalysisTaskController._chartDataChanged): When end points are not found, try showing outliers.
+        This will cause chartData to be modified so just exit early and wait for getting called again.
+
+        * public/v2/interactive-chart.js:
+        (App.InteractiveChartComponent._rangesChanged): The code to compute start and end time has been moved
+        to App.Pane.ranges.
+
+        * public/v2/manifest.js:
+        (App.Manifest._formatFetchedData): Added unfiltered time series as new properties as they are now used
+        to compute the end points of analysis tasks when their end points are outliers.
+
+2015-10-07  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
</ins><span class="cx">         Unreviewed. Fix a typo in r190645.
</span><span class="cx"> 
</span><span class="cx">         * public/include/db.php:
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicadminfetchfromremotephp"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/admin/fetch-from-remote.php (190675 => 190676)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/admin/fetch-from-remote.php        2015-10-07 18:26:19 UTC (rev 190675)
+++ trunk/Websites/perf.webkit.org/public/admin/fetch-from-remote.php        2015-10-07 18:38:07 UTC (rev 190676)
</span><span class="lines">@@ -38,6 +38,6 @@
</span><span class="cx">     return @file_get_contents($remote_url, false, $context);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-main(array_get($_SERVER, 'PATH_INFO', ''));
</del><ins>+main(array_get($_SERVER, 'REQUEST_URI', ''));
</ins><span class="cx"> 
</span><span class="cx"> ?&gt;
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv2appjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v2/app.js (190675 => 190676)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v2/app.js        2015-10-07 18:26:19 UTC (rev 190675)
+++ trunk/Websites/perf.webkit.org/public/v2/app.js        2015-10-07 18:38:07 UTC (rev 190676)
</span><span class="lines">@@ -378,6 +378,12 @@
</span><span class="cx">         this.set('metric', result.metric);
</span><span class="cx">         this._setNewChartData(result.data);
</span><span class="cx">     },
</span><ins>+    _showOutlierChanged: function ()
+    {
+        var chartData = this.get('chartData');
+        if (chartData)
+            this._setNewChartData(chartData);
+    }.observes('showOutlier'),
</ins><span class="cx">     _setNewChartData: function (chartData)
</span><span class="cx">     {
</span><span class="cx">         var newChartData = {};
</span><span class="lines">@@ -420,15 +426,43 @@
</span><span class="cx">         var metricId = this.get('metricId');
</span><span class="cx">         var self = this;
</span><span class="cx">         this.get('store')
</span><del>-            .find('analysisTask', {platform: platformId, metric: metricId})
</del><ins>+            .findAll('analysisTask') // FIXME: Fetch only analysis tasks relevant for this pane.
</ins><span class="cx">             .then(function (tasks) {
</span><del>-                self.set('analyticRanges', tasks.filter(function (task) { return task.get('startRun') &amp;&amp; task.get('endRun'); }));
</del><ins>+                self.set('analyticRanges', tasks.filter(function (task) {
+                    return task.get('platform').get('id') == platformId
+                        &amp;&amp; task.get('metric').get('id') == metricId
+                        &amp;&amp; task.get('startRun') &amp;&amp; task.get('endRun');
+                }));
</ins><span class="cx">             });
</span><span class="cx">     },
</span><span class="cx">     ranges: function ()
</span><span class="cx">     {
</span><del>-        return this.getWithDefault('analyticRanges', []).concat(this.getWithDefault('testRangeCandidates', []));
-    }.property('analyticRanges', 'testRangeCandidates'),
</del><ins>+        var chartData = this.get('chartData');
+        if (!chartData || !chartData.unfilteredCurrentTimeSeries)
+            return [];
+
+        function midPoint(firstPoint, secondPoint) {
+            if (firstPoint &amp;&amp; secondPoint)
+                return (+firstPoint.time + +secondPoint.time) / 2;
+            if (firstPoint)
+                return firstPoint.time;
+            return secondPoint.time;
+        }
+
+        var timeSeries = chartData.unfilteredCurrentTimeSeries;
+        var ranges = this.getWithDefault('analyticRanges', []);
+        var testranges = this.getWithDefault('testRangeCandidates', []);
+        return this.getWithDefault('analyticRanges', []).concat(this.getWithDefault('testRangeCandidates', [])).map(function (range) {
+            var start = timeSeries.findPointByMeasurementId(range.get('startRun'));
+            var end = timeSeries.findPointByMeasurementId(range.get('endRun'));
+
+            return Ember.ObjectProxy.create({
+                content: range,
+                startTime: start ? midPoint(timeSeries.previousPoint(start), start) : null,
+                endTime: end ? midPoint(end, timeSeries.nextPoint(end)) : null,
+            });
+        });
+    }.property('chartData', 'analyticRanges', 'testRangeCandidates'),
</ins><span class="cx">     _isValidId: function (id)
</span><span class="cx">     {
</span><span class="cx">         if (typeof(id) == &quot;number&quot;)
</span><span class="lines">@@ -1013,12 +1047,7 @@
</span><span class="cx">         },
</span><span class="cx">         toggleShowOutlier: function ()
</span><span class="cx">         {
</span><del>-            var pane = this.get('model');
-            pane.toggleProperty('showOutlier');
-            var chartData = pane.get('chartData');
-            if (!chartData)
-                return;
-            pane._setNewChartData(chartData);
</del><ins>+            this.get('model').toggleProperty('showOutlier');
</ins><span class="cx">         },
</span><span class="cx">         createAnalysisTask: function ()
</span><span class="cx">         {
</span><span class="lines">@@ -1285,13 +1314,15 @@
</span><span class="cx">             return null;
</span><span class="cx"> 
</span><span class="cx">         var currentTimeSeries = chartData.current;
</span><del>-        if (!currentTimeSeries)
-            return null; // FIXME: Report an error.
</del><ins>+        Ember.assert('chartData.current should always be defined', currentTimeSeries);
</ins><span class="cx"> 
</span><span class="cx">         var start = currentTimeSeries.findPointByMeasurementId(this.get('model').get('startRun'));
</span><span class="cx">         var end = currentTimeSeries.findPointByMeasurementId(this.get('model').get('endRun'));
</span><del>-        if (!start || !end)
-            return null; // FIXME: Report an error.
</del><ins>+        if (!start || !end) {
+            if (!pane.get('showOutlier'))
+                pane.set('showOutlier', true);
+            return;
+        }
</ins><span class="cx"> 
</span><span class="cx">         var highlightedItems = {};
</span><span class="cx">         highlightedItems[start.measurement.id()] = true;
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv2interactivechartjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v2/interactive-chart.js (190675 => 190676)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v2/interactive-chart.js        2015-10-07 18:26:19 UTC (rev 190675)
+++ trunk/Websites/perf.webkit.org/public/v2/interactive-chart.js        2015-10-07 18:38:07 UTC (rev 190676)
</span><span class="lines">@@ -618,24 +618,11 @@
</span><span class="cx">     }.observes('highlightedItems'),
</span><span class="cx">     _rangesChanged: function ()
</span><span class="cx">     {
</span><del>-        if (!this._currentTimeSeries)
-            return;
-
-        function midPoint(firstPoint, secondPoint) {
-            if (firstPoint &amp;&amp; secondPoint)
-                return (+firstPoint.time + +secondPoint.time) / 2;
-            if (firstPoint)
-                return firstPoint.time;
-            return secondPoint.time;
-        }
-        var currentTimeSeries = this._currentTimeSeries;
</del><span class="cx">         var linkRoute = this.get('rangeRoute');
</span><span class="cx">         this.set('rangeBars', (this.get('ranges') || []).map(function (range) {
</span><del>-            var start = currentTimeSeries.findPointByMeasurementId(range.get('startRun'));
-            var end = currentTimeSeries.findPointByMeasurementId(range.get('endRun'));
</del><span class="cx">             return Ember.Object.create({
</span><del>-                startTime: midPoint(currentTimeSeries.previousPoint(start), start),
-                endTime: midPoint(end, currentTimeSeries.nextPoint(end)),
</del><ins>+                startTime: range.get('startTime'),
+                endTime: range.get('endTime'),
</ins><span class="cx">                 range: range,
</span><span class="cx">                 left: null,
</span><span class="cx">                 right: null,
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv2manifestjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v2/manifest.js (190675 => 190676)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v2/manifest.js        2015-10-07 18:26:19 UTC (rev 190675)
+++ trunk/Websites/perf.webkit.org/public/v2/manifest.js        2015-10-07 18:38:07 UTC (rev 190676)
</span><span class="lines">@@ -361,12 +361,20 @@
</span><span class="cx">         var currentTimeSeries = configurations.current.timeSeriesByCommitTime(false);
</span><span class="cx">         var baselineTimeSeries = configurations.baseline ? configurations.baseline.timeSeriesByCommitTime(false) : null;
</span><span class="cx">         var targetTimeSeries = configurations.target ? configurations.target.timeSeriesByCommitTime(false) : null;
</span><del>-        var unfilteredCurrentTimeSeries, unfilteredBaselineTimeSeries, unfilteredTargetTimeSeries;
</del><span class="cx"> 
</span><ins>+        var unfilteredCurrentTimeSeries = configurations.current.timeSeriesByCommitTime(true);
+        var unfilteredBaselineTimeSeries = configurations.baseline ? configurations.baseline.timeSeriesByCommitTime(true) : null;
+        var unfilteredTargetTimeSeries = configurations.target ? configurations.target.timeSeriesByCommitTime(true) : null;
+
</ins><span class="cx">         return {
</span><span class="cx">             current: currentTimeSeries,
</span><span class="cx">             baseline: baselineTimeSeries,
</span><span class="cx">             target: targetTimeSeries,
</span><ins>+
+            unfilteredCurrentTimeSeries: unfilteredCurrentTimeSeries,
+            unfilteredBaselineTimeSeries: unfilteredBaselineTimeSeries,
+            unfilteredTargetTimeSeries: unfilteredTargetTimeSeries,
+
</ins><span class="cx">             formatWithDeltaAndUnit: function (value, delta)
</span><span class="cx">             {
</span><span class="cx">                 return this.formatter(value) + (delta &amp;&amp; !isNaN(delta) ? ' \u00b1 ' + deltaFormatterWithoutSign(delta) : '');
</span><span class="lines">@@ -376,11 +384,6 @@
</span><span class="cx">             smallerIsBetter: smallerIsBetter,
</span><span class="cx">             showOutlier: function (show)
</span><span class="cx">             {
</span><del>-                if (!unfilteredCurrentTimeSeries) {
-                    unfilteredCurrentTimeSeries = configurations.current.timeSeriesByCommitTime(true);
-                    unfilteredBaselineTimeSeries = configurations.baseline ? configurations.baseline.timeSeriesByCommitTime(true) : null;
-                    unfilteredTargetTimeSeries = configurations.target ? configurations.target.timeSeriesByCommitTime(true) : null;
-                }
</del><span class="cx">                 this.current = show ? unfilteredCurrentTimeSeries : currentTimeSeries;
</span><span class="cx">                 this.baseline = show ? unfilteredBaselineTimeSeries : baselineTimeSeries;
</span><span class="cx">                 this.target = show ? unfilteredTargetTimeSeries : targetTimeSeries;
</span></span></pre>
</div>
</div>

</body>
</html>