From c2ce0fc1d778321c0b2002ad913086c8337887db Mon Sep 17 00:00:00 2001 From: Dhananjay Nakrani Date: Sun, 30 Aug 2015 10:52:41 -0700 Subject: [PATCH 01/13] Add Servo in in Puller and builder. --- .gitignore | 2 ++ slave/build.py | 22 ++++++++++++++++++++++ slave/puller.py | 2 ++ 3 files changed, 26 insertions(+) diff --git a/.gitignore b/.gitignore index 581a720..92ce860 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,5 @@ benchmarks/asmjs-apps/zlib/minigzipsh driver/awfy.config slave/awfy.config **/*-results/ + +output/ diff --git a/slave/build.py b/slave/build.py index 0c1f2a0..ddb0a79 100644 --- a/slave/build.py +++ b/slave/build.py @@ -241,6 +241,26 @@ class V8Builder(Builder): def binary(self): return os.path.join(self.objdir(), 'd8') +class ServoBuilder(Builder): + def __init__(self, config, folder): + super(ServoBuilder, self).__init__(config, folder); + # Some other config here + + def retrieveInfo(self): + info = {} + info["engine_type"] = "servo" + return info + + def objdir(self): + return os.path.join(self.folder, 'target') + + def binary(self): + return os.path.join(self.objdir(), 'release', 'servo') + + def make(self): + args = [os.path.join('.', self.folder, 'mach'), 'build' ,'--release'] + Run(args, self.env.get()) + def getBuilder(config, path): # fingerprint the known builders if os.path.exists(os.path.join(path, "js", "src")): @@ -249,6 +269,8 @@ def getBuilder(config, path): return WebkitBuilder(config, path) if os.path.exists(os.path.join(path, "v8", "LICENSE.v8")): return V8Builder(config, path) + if os.path.exists(os.path.join(path, "mach")): + return ServoBuilder(config, path) raise Exception("Unknown builder") diff --git a/slave/puller.py b/slave/puller.py index 65d5761..504320b 100644 --- a/slave/puller.py +++ b/slave/puller.py @@ -157,6 +157,8 @@ def getPuller(repo, path): repo = "http://hg.mozilla.org/integration/mozilla-inbound" elif repo == "webkit": repo = "https://svn.webkit.org/repository/webkit/trunk" + elif repo == "servo": + repo = "https://github.com/servo/servo.git" if "hg." in repo: return HG(repo, path) From ce6e614097a56bda5ebf412c62dea5678ad74603 Mon Sep 17 00:00:00 2001 From: Dhananjay Nakrani Date: Sun, 6 Sep 2015 14:57:24 -0700 Subject: [PATCH 02/13] Execute `mach build` from servo's root directory. --- slave/build.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/slave/build.py b/slave/build.py index ddb0a79..360fbf6 100644 --- a/slave/build.py +++ b/slave/build.py @@ -258,8 +258,16 @@ class ServoBuilder(Builder): return os.path.join(self.objdir(), 'release', 'servo') def make(self): - args = [os.path.join('.', self.folder, 'mach'), 'build' ,'--release'] - Run(args, self.env.get()) + # Remember cwd + cwd = os.getcwd() + # cd into servo's root directory. Requirement of 'mach build' + os.chdir(self.folder) + try: + args = [os.path.join('.', 'mach'), 'build' ,'--release'] + Run(args, self.env.get()) + finally: + # Go back to the cwd + os.chdir(cwd) def getBuilder(config, path): # fingerprint the known builders From d7fe15534eaf003859e3e1b319231b67cfd79a6f Mon Sep 17 00:00:00 2001 From: Dhananjay Nakrani Date: Mon, 7 Sep 2015 10:19:39 -0700 Subject: [PATCH 03/13] Use utils.FolderChanger() to temporarily change directory. --- slave/build.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/slave/build.py b/slave/build.py index 360fbf6..6839bdf 100644 --- a/slave/build.py +++ b/slave/build.py @@ -258,16 +258,9 @@ class ServoBuilder(Builder): return os.path.join(self.objdir(), 'release', 'servo') def make(self): - # Remember cwd - cwd = os.getcwd() - # cd into servo's root directory. Requirement of 'mach build' - os.chdir(self.folder) - try: + with utils.FolderChanger(self.folder): args = [os.path.join('.', 'mach'), 'build' ,'--release'] Run(args, self.env.get()) - finally: - # Go back to the cwd - os.chdir(cwd) def getBuilder(config, path): # fingerprint the known builders From 7fc005fb3ea9c44864acd3c013114a1c5f8ffd6d Mon Sep 17 00:00:00 2001 From: Dhananjay Nakrani Date: Mon, 7 Sep 2015 10:29:23 -0700 Subject: [PATCH 04/13] Use 'components/servo' to identify servo's repository instead of 'mach'. --- slave/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slave/build.py b/slave/build.py index 6839bdf..a950dac 100644 --- a/slave/build.py +++ b/slave/build.py @@ -270,7 +270,7 @@ def getBuilder(config, path): return WebkitBuilder(config, path) if os.path.exists(os.path.join(path, "v8", "LICENSE.v8")): return V8Builder(config, path) - if os.path.exists(os.path.join(path, "mach")): + if os.path.exists(os.path.join(path, "components", "servo")): return ServoBuilder(config, path) raise Exception("Unknown builder") From 5707594b83266e99f8d4a00ca79b0d92fa7905b3 Mon Sep 17 00:00:00 2001 From: Dhananjay Nakrani Date: Thu, 24 Sep 2015 10:59:02 -0700 Subject: [PATCH 05/13] Send reponse back at the end of the request. Servo goes into infinite requests mode Issue #7725 (Servo). --- slave/server.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/slave/server.py b/slave/server.py index 08aff45..69f8db5 100644 --- a/slave/server.py +++ b/slave/server.py @@ -20,6 +20,7 @@ class FakeHandler(SimpleHTTPRequestHandler): parsedParams = urlparse.urlparse(self.path) self.localBenchmark(parsedParams.query) + self.send_response(200) def do_POST(self): length = int(self.headers.getheader('content-length', 0)) @@ -29,6 +30,7 @@ class FakeHandler(SimpleHTTPRequestHandler): return self.localBenchmark(postdata) + self.send_response(200) def localBenchmark(self, query = None): if self.path.startswith("/submit"): From 3c0a60e25126e81ad11f69215b33596b22c7c0eb Mon Sep 17 00:00:00 2001 From: Dhananjay Nakrani Date: Thu, 24 Sep 2015 13:27:56 -0700 Subject: [PATCH 06/13] Overwrite Shell=False for Servo. --- slave/build.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/slave/build.py b/slave/build.py index a950dac..562365d 100644 --- a/slave/build.py +++ b/slave/build.py @@ -262,6 +262,16 @@ class ServoBuilder(Builder): args = [os.path.join('.', 'mach'), 'build' ,'--release'] Run(args, self.env.get()) + def build(self, puller): + # Call parent's build + super(ServoBuilder, self).build(puller) + # Read info.json file back and modify only shell=False. + with open(os.path.join(self.folder, "info.json"), 'r+') as info_file: + info = json.load(info_file) + info["shell"] = False + info_file.seek(0) + json.dump(info, info_file) + def getBuilder(config, path): # fingerprint the known builders if os.path.exists(os.path.join(path, "js", "src")): From e54774645bd15cdadc3cb5e8131bb78ca67d6d13 Mon Sep 17 00:00:00 2001 From: Dhananjay Nakrani Date: Fri, 25 Sep 2015 11:34:40 -0700 Subject: [PATCH 07/13] Add executor for Servo. --- slave/configs.py | 2 ++ slave/execute.py | 3 ++- slave/executors.py | 24 +++++++++++++++++++++++- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/slave/configs.py b/slave/configs.py index 5dd5c96..97ca262 100644 --- a/slave/configs.py +++ b/slave/configs.py @@ -23,6 +23,8 @@ class Default(object): pass elif engine == "ie": pass + elif engine == "servo": + pass else: self.omit_ = True diff --git a/slave/execute.py b/slave/execute.py index 0408b34..0a3a157 100644 --- a/slave/execute.py +++ b/slave/execute.py @@ -54,7 +54,8 @@ if options.mode_rules is None: "chrome,default:v8", "chrome,turbofan:v8-turbofan", "webkit,default:jsc", - "native,default:clang" + "native,default:clang", + "servo,default:servo" ] #TODO:remove diff --git a/slave/executors.py b/slave/executors.py index 50472b2..9588f28 100644 --- a/slave/executors.py +++ b/slave/executors.py @@ -2,6 +2,7 @@ import runners import time import os import sys +import json sys.path.insert(1, '../driver') import utils @@ -45,7 +46,7 @@ class BrowserExecutor(object): os.unlink("results") def waitForResults(self): - timeout = int(utils.config.get('main', 'timeout')) * 60 + timeout = utils.config.Timeout * 60 while not os.path.exists("results") and timeout > 0: time.sleep(10) timeout -= 10 @@ -86,6 +87,25 @@ class FirefoxExecutor(BrowserExecutor): # kill browser runner.kill(process) +class ServoExecutor(BrowserExecutor): + def execute(self, page, env, args): + runner = runners.getRunner(self.engineInfo["platform"], {}) + + # kill all possible running instances. + runner.killall("servo") + + # reset the result + self.resetResults() + + # start browser + process = runner.start(self.engineInfo["binary"], args, env) + + # wait for results + self.waitForResults() + + # kill browser + runner.kill(process) + #class NativeExecutor(Executor): # @@ -97,4 +117,6 @@ def getExecutor(engineInfo): return ShellExecutor(engineInfo) if engineInfo["engine_type"] == "firefox" and not engineInfo["shell"]: return FirefoxExecutor(engineInfo) + if engineInfo["engine_type"] == "servo": + return ServoExecutor(engineInfo) From a2bf20294d898d3a99a5622993d87d14fea0eb98 Mon Sep 17 00:00:00 2001 From: Dhananjay Nakrani Date: Thu, 1 Oct 2015 13:17:47 -0700 Subject: [PATCH 08/13] Change hack around Shell=False for Servo Builds. Remove extra driver inclusion. --- slave/build.py | 15 ++++----------- slave/executors.py | 1 - website/index.html | 2 +- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/slave/build.py b/slave/build.py index 562365d..13f86fb 100644 --- a/slave/build.py +++ b/slave/build.py @@ -96,7 +96,9 @@ class Builder(object): info = self.retrieveInfo() info["revision"] = puller.identify() - info["shell"] = True + # Deafult 'shell' to True only if it isn't set yet! + if 'shell' not in info: + info["shell"] = True info["binary"] = os.path.abspath(self.binary()) fp = open(os.path.join(self.folder, "info.json"), "w") @@ -249,6 +251,7 @@ class ServoBuilder(Builder): def retrieveInfo(self): info = {} info["engine_type"] = "servo" + info['shell'] = False return info def objdir(self): @@ -262,16 +265,6 @@ class ServoBuilder(Builder): args = [os.path.join('.', 'mach'), 'build' ,'--release'] Run(args, self.env.get()) - def build(self, puller): - # Call parent's build - super(ServoBuilder, self).build(puller) - # Read info.json file back and modify only shell=False. - with open(os.path.join(self.folder, "info.json"), 'r+') as info_file: - info = json.load(info_file) - info["shell"] = False - info_file.seek(0) - json.dump(info, info_file) - def getBuilder(config, path): # fingerprint the known builders if os.path.exists(os.path.join(path, "js", "src")): diff --git a/slave/executors.py b/slave/executors.py index 9588f28..01be823 100644 --- a/slave/executors.py +++ b/slave/executors.py @@ -4,7 +4,6 @@ import os import sys import json -sys.path.insert(1, '../driver') import utils class ShellExecutor(object): diff --git a/website/index.html b/website/index.html index d8b3e2f..c229243 100755 --- a/website/index.html +++ b/website/index.html @@ -3,7 +3,7 @@ - ARE WE FAST YET? + Servo Benchmarking From 6c0824f542dbea329cf104e5246ca9b1a6e9ba9a Mon Sep 17 00:00:00 2001 From: Dhananjay Nakrani Date: Mon, 19 Oct 2015 11:51:06 -0700 Subject: [PATCH 09/13] Add DB migration for Servo. --- database/migration-1.php | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 database/migration-1.php diff --git a/database/migration-1.php b/database/migration-1.php new file mode 100644 index 0000000..07c3ce9 --- /dev/null +++ b/database/migration-1.php @@ -0,0 +1,30 @@ + Date: Wed, 21 Oct 2015 11:19:14 -0700 Subject: [PATCH 10/13] Add index page and corresponding js for Servo-AWFY. --- website/servo/awfy.js | 1059 ++++++++++++++++++++++++++++++++++++++ website/servo/index.html | 153 ++++++ 2 files changed, 1212 insertions(+) create mode 100755 website/servo/awfy.js create mode 100755 website/servo/index.html diff --git a/website/servo/awfy.js b/website/servo/awfy.js new file mode 100755 index 0000000..145687d --- /dev/null +++ b/website/servo/awfy.js @@ -0,0 +1,1059 @@ +// vim: set ts=4 sw=4 tw=99 et: +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; +var AWFY = { }; + +AWFY.DEFAULT_MACHINE_ID = 1; +AWFY.refreshTime = 60 * 5; +AWFY.machineId = 0; +AWFY.hasLegend = false; +AWFY.panes = []; +AWFY.queryParams = null; +AWFY.aggregate = null; +AWFY.xhr = []; +AWFY.view = 'none'; +AWFY.suiteName = null; +AWFY.subtest = null; +AWFY.lastHash = null; +AWFY.lastRefresh = 0; + +AWFY.request = function (files, callback) { + var url = window.location.protocol + '//' + + window.location.host; + if (url[url.length - 1] != '/') + url += '/'; + url += 'data.php?file='; + + var count = 0; + var received = new Array(files.length); + var done = (function (jqXHR, textStatus) { + count++; + if (count == files.length) + callback(received); + this.xhr.splice(this.xhr.lastIndexOf(jqXHR), 1); + }).bind(this); + + for (var i = 0; i < files.length; i++) { + var success = (function (index) { + return function (data, textStatus, jqXHR) { + received[index] = data; + }; + })(i); + var req = { async: true, + complete: done, + success: success, + cache: false + }; + this.xhr.push($.ajax(url + files[i] + '.json', req)); + } +} + +AWFY.pushState = function () { + // Build URL query. + var vars = [] + vars.push('machine=' + this.machineId); + + if (this.view == 'breakdown') { + vars.push('view=breakdown'); + vars.push('suite=' + this.suiteName); + } + if (this.view == 'single') { + vars.push('view=single'); + vars.push('suite=' + this.suiteName); + if (this.subtest) + vars.push('subtest=' + this.subtest); + if (this.start && this.end) { + vars.push('start='+this.start); + vars.push('end='+this.end); + } + } + + if ($('#about').is(':visible')) + vars.push('about=1'); + + var text = '#' + vars.join('&'); + this.lastHash = text; + if (window.history.pushState) + window.history.pushState(null, 'AreWeFastYet', text); + else + window.location.hash = '#' + text; +} + +AWFY.loadAggregateGraph = function (blobgraph) { + if (!blobgraph) + return; + var lines = []; + var info = []; + for (var i = 0; i < blobgraph.lines.length; i++) { + var blobline = blobgraph.lines[i]; + + var points = []; + var k = 0; + for (var j = 0; j < blobline.data.length; j++) { + var point = blobline.data[j]; + // When there is no point, we normally just push a null point. + // This results in a line stop. Now e.g. firefox OS has 4 different + // engines reporting on different times. So every point has a gap. + // To counteract this we still draw a line (by not giving null points), + // if there has been less than the amount of lines (*1.5). + if (!point) { + if (k++ < blobgraph.lines.length*1.5) + continue + } else { + k = 0; + } + var score = point && point[0] + ? point[0] + : null; + points.push([j, score]); + } + + var mode = AWFYMaster.modes[blobline.modeid]; + if (!mode) + continue; + + var line = { color: mode.color, data: points }; + lines.push(line); + info.push(blobline); + } + + var graph = { lines: lines, + direction: blobgraph.direction, + aggregate: blobgraph.aggregate, + timelist: blobgraph.timelist, + earliest: blobgraph.earliest, + info: info + }; + return graph; +} + +AWFY.displayNewGraph = function (id, domid, graph) { + var elt = $('#' + domid + '-graph'); + var title = $('#' + domid + '-title'); + if (!elt.length) + return; + if (!graph) { + this.aggregate[id] = undefined; + elt.hide(); + title.hide(); + return; + } + elt.show(); + title.show(); + var display = elt.data('awfy-display'); + if (!display) { + display = new Display(this, id, domid, elt); + elt.data('awfy-display', display); + } + if (display.shouldRefresh()) { + display.setup(graph); + display.draw(); + } + this.aggregate[id] = graph; + if (this.start && this.end) { + AWFY.requestZoom(display, "condensed", this.start, this.end) + display.zoomInfo.level = 'month'; + } +} + +AWFY.drawLegend = function () { + // Draw the legend if needed. + if (this.hasLegend) + return; + + var legend = $("#legend"); + + legend.empty(); + + var modes = []; + for (var modename in AWFYMaster.modes) { + var mode = AWFYMaster.modes[modename]; + // hack - strip jm+ti, bc + if (modename == 12 || modename == 15) + continue; + if (AWFY.machineId != 14 && modename == 16) + continue; + if (!mode.used) + continue; + modes.push(mode); + } + + for (var i = 0; i < modes.length; i++) { + var mode = modes[i]; + var vendor = AWFYMaster.vendors[mode.vendor_id]; + if (!vendor) + continue; + var item = $('
  • '); + var link = $('' + + vendor.browser + + ' (' + + mode.name + + ')'); + + var onClick = (function (awfy, mode, link) { + return (function () { + if (mode.hidden) { + mode.hidden = false; + link.css('color', '#000000'); + } else { + mode.hidden = true; + link.css('color', '#cccccc'); + } + for (var i = 0; i < this.panes.length; i++) { + var elt = this.panes[i]; + var display = elt.data('awfy-display'); + if (!display) + continue; + display.draw(); + } + return false; + }).bind(awfy); + })(this, mode, link); + link.click(onClick); + + if (mode.hidden) + link.css('color', '#cccccc'); + else + link.css('color', '#000000'); + + link.appendTo(item); + item.appendTo(legend); + } + + this.hasLegend = true; +} + +AWFY.computeBreakdown = function (data, id, domid) { + var blob = typeof data == "string" + ? JSON.parse(data) + : data; + + // Should we handle version changes better? + if (blob.version != AWFYMaster.version) { + window.location.reload(); + return; + } + + var graph = this.loadAggregateGraph(blob['graph']); + this.displayNewGraph(id, domid, graph); +} + +AWFY.computeAggregate = function (received) { + var blob = typeof received[0] == "string" + ? JSON.parse(received[0]) + : received[0]; + + // Should we handle version changes better? + if (blob.version != AWFYMaster.version) { + window.location.reload(); + return; + } + + var graphs = { }; + for (var name in blob.graphs) { + var blobgraph = blob.graphs[name]; + if (!blobgraph) + continue; + graphs[name] = this.loadAggregateGraph(blobgraph); + } + + // Save this for if/when we need to zoom out. + this.aggregate = graphs; + + for (var id in graphs) { + this.displayNewGraph(id, id, graphs[id]); + } + + this.drawLegend(); +} + +AWFY.mergeJSON = function (blobs) { + var lines = { }; + var timelist = []; + + // We're guaranteed the blobs are in sorted order, which makes this simpler. + for (var i = 0; i < blobs.length; i++) { + var blob = blobs[i]; + + // Should we handle version changes better? + if (blob.version != AWFYMaster.version) { + window.location.reload(); + return; + } + + for (var j = 0; j < blob.graph.lines.length; j++) { + var blobline = blob.graph.lines[j]; + + var line = lines[blobline.modeid]; + if (!line) { + var points = []; + var info = []; + + // We have to pre-fill the array with slots for each blob + // we may have missed. + for (var k = 0; k < timelist.length; k++) { + points.push(null); + info.push(null); + } + + line = { points: points, info: info }; + lines[blobline.modeid] = line; + } + + var points = line.points; + var info = line.info; + + for (var k = 0; k < blobline.data.length; k++) { + var point = blobline.data[k]; + var score = point && point[0] + ? point[0] + : null; + points.push([timelist.length + k, score]); + info.push(point); + } + } + + for (var j = 0; j < blob.graph.timelist.length; j++) + timelist.push(blob.graph.timelist[j]); + + // If we missed updating any line, pre-fill it with null points. + for (var modeid in lines) { + var line = lines[modeid]; + if (line.points.length == timelist.length) + continue; + for (var j = line.points.length; j < timelist.length; j++) { + line.points.push(null); + line.info.push(null); + } + } + } + + var actual = []; + var info = []; + for (var modename in lines) { + if (!(modename in AWFYMaster.modes)) + continue; + var line = { data: lines[modename].points, + color: AWFYMaster.modes[modename].color + }; + actual.push(line); + info.push({ 'modeid': parseInt(modename), + 'data': lines[modename].info }); + } + + var graph = { lines: actual, + aggregate: false, + timelist: timelist, + info: info + }; + return graph; +} + +AWFY.condense = function (graph, max) { + if (graph.timelist.length <= max) + return graph; + + var slice = graph.timelist.length / max; + + var timelist = []; + var lines = []; + var info = []; + + // Setup the new structures. + for (var i = 0; i < graph.lines.length; i++) { + var newline = { 'color': graph.lines[i].color, + 'data': [] + }; + var newinfo = { 'modeid': graph.info[i].modeid, + 'data': [] + }; + lines.push(newline); + info.push(newinfo); + } + + var pos = 0; + for (var i = 0; i < max; i++) { + var start = Math.round(pos); + + for (var lineno = 0; lineno < lines.length; lineno++) { + var oldinfo = graph.info[lineno]; + var newline = lines[lineno]; + var newinfo = info[lineno]; + + var average = 0; + var count = 0; + var first = null; + var last = null; + var suite_version = null + var id = null + for (var j = start; j < pos + slice && j < oldinfo.data.length; j++) { + var point = oldinfo.data[j]; + if (!point || !point[0]) + continue; + if (!first) + first = point[1]; + if (point.length > 1 && point[2]) + last = point[2]; + else + last = first + average = ((average * count) + point[0]) / (count + 1); + suite_version = point[3] + id = point[4] + count += 1; + } + + var score = average ? average : null; + id = count == 1 ? id : null + newline.data.push([timelist.length, score]); + newinfo.data.push([average, first, last, suite_version, id]) + } + + timelist.push(graph.timelist[start]); + pos += slice; + } + + return { info: info, + lines: lines, + timelist: timelist, + direction: graph.direction }; +} + +AWFY.trim = function (graph, start, end) { + var timelist = []; + var lines = []; + var infos = []; + + // Setup the new structures. + for (var i = 0; i < graph.lines.length; i++) { + var newline = { 'color': graph.lines[i].color, + 'data': [] + }; + var newinfo = { 'modeid': graph.info[i].modeid, + 'data': [] + }; + lines.push(newline); + infos.push(newinfo); + } + + // Whether |end| is inclusive is not really clear, actually. + for (var i = start; i < end; i++) + timelist.push(graph.timelist[i]); + + for (var i = 0; i < graph.lines.length; i++) { + var oldline = graph.lines[i]; + var oldinfo = graph.info[i]; + var line = lines[i]; + var info = infos[i]; + for (var j = start; j < end; j++) { + var point = oldline.data[j]; + line.data.push([j - start, point ? point[1] : null]); + info.data.push(oldinfo.data[j]); + } + } + + return { lines: lines, + info: infos, + timelist: timelist, + direction: graph.direction, + start: start, + end: end + }; +} + +AWFY.computeZoom = function (display, received, start, end) { + // Get JSON blobs for each received text. + var blobs = []; + for (var i = 0; i < received.length; i++) { + if (!received[i]) + continue; + if (typeof received[i] == "string") + blobs.push(JSON.parse(received[i])); + else + blobs.push(received[i]); + } + + if (!blobs.length) { + display.cancelZoom(); + return; + } + + var graph = this.mergeJSON(blobs); + display.completeZoom(graph, start, end); +} + +AWFY.findX = function (graph, time) { + for (var i = 0; i < graph.timelist.length; i++) { + if (graph.timelist[i] >= time) + break; + } + return i; +} + +AWFY.requestZoom = function (display, kind, start_t, end_t) { + // Figure out the list of dates we'll need to query. + var files = []; + + var start = new Date(start_t * 1000); + var end = new Date(end_t * 1000); + for (var year = start.getUTCFullYear(); year <= end.getUTCFullYear(); year++) { + var firstMonth = (year == start.getUTCFullYear()) + ? start.getUTCMonth() + 1 + : 1; + var lastMonth = (year == end.getUTCFullYear()) + ? end.getUTCMonth() + 1 + : 12; + for (var month = firstMonth; month <= lastMonth; month++) { + var name = kind + '-' + + display.id + '-' + + this.machineId + '-' + + year + '-' + + month; + if (this.view == 'breakdown') + name = 'bk-' + name; + else if (this.view == 'single' && this.subtest) + name = 'bk-' + name; + files.push(name); + } + } + + var zoom = function (received) { + this.computeZoom(display, received, start_t, end_t); + } + + this.request(files, zoom.bind(this)); +} + +AWFY.trackZoom = function (start, end) { + // Only track in single modus + if (this.view != 'single') + return; + + this.start = start; + this.end = end; + this.pushState(); +} + +AWFY.showOverview = function () { + this.reset('overview'); + + $('#breakdown').empty(); + $('#breakdown').hide(); + + $('.graph-container').show(); + + this.suiteName = null; + this.subtest = null + this.start = null + this.end = null + this.panes = [$('#dromaeo-graph')]; + + this.request(['aggregate-' + this.machineId], this.computeAggregate.bind(this)); + this.lastRefresh = Date.now(); +} + +AWFY.showBreakdown = function (name) { + this.reset('breakdown'); + + // Clear the breakdown view. + var breakdown = $('#breakdown'); + breakdown.empty() + + $('.graph-container').hide(); + breakdown.show(); + + this.suiteName = name; + this.start = null + this.end = null + this.subtest = null; + this.panes = []; + + var total = 0; + + // Create a div for each sub-test. + var suite = AWFYMaster.suites[name]; + for (var i = 0; i < suite.tests.length; i++) { + var test = suite.tests[i]; + var id = name + '-' + test; + var domid = id.replace(/ /g,'-').replace(/\./g, '-'); + ( function (name, test) { + var title = $('
    ').click( + (function (event) { + this.showSingle(name, test, null, null); + this.pushState(); + return false; + }).bind(this)) + .html('' + id + '') + .appendTo(breakdown); + title.hide(); + }.bind(this) )(name, test) + var div = $('
    '); + div.appendTo(breakdown); + div.hide(); + + this.panes.push(div); + + var callback = (function (id, domid) { + return (function (received) { + if (received[0]) + this.computeBreakdown(received[0], id, domid); + if (++total == suite.tests.length) + this.drawLegend(); + }).bind(this); + }).bind(this)(id, domid); + + // Fire off an XHR request for each test. + var file = 'bk-aggregate-' + id + '-' + this.machineId; + this.request([file], callback); + } + this.lastRefresh = Date.now(); +} + +AWFY.showSingle = function (name, subtest, start, end) { + this.reset('single'); + + // Clear the breakdown view. + var breakdown = $('#breakdown'); + breakdown.empty() + + $('.graph-container').hide(); + breakdown.show(); + + this.suiteName = name; + this.subtest = subtest; + this.start = start; + this.end = end; + this.panes = []; + + // Test existance of subtest + var suite = AWFYMaster.suites[name]; + var found = false; + for (var i = 0; i < suite.tests.length; i++) { + var test = suite.tests[i]; + if (subtest == test) { + found = true; + break; + } + } + + if (found) { + var id = name + '-' + test; + var domid = id.replace(/ /g,'-').replace(/\./g, '-'); + var title = $('
    ').html('' + id + '').appendTo(breakdown); + title.hide(); + var div = $('
    '); + div.appendTo(breakdown); + div.hide(); + $('

    ').appendTo(breakdown); + + this.panes.push(div); + + var callback = (function (id, domid) { + return (function (received) { + if (received[0]) + this.computeBreakdown(received[0], id, domid); + this.drawLegend(); + }).bind(this); + }).bind(this)(id, domid); + + var file = 'bk-aggregate-' + id + '-' + this.machineId; + this.request([file], callback); + } else { + var id = name; + var domid = id.replace(/ /g,'-').replace(/\./g, '-') + "-single"; + var title = $('
    ').html('' + id + '').appendTo(breakdown); + title.hide(); + var div = $('
    '); + div.appendTo(breakdown); + div.hide(); + $('

    ').appendTo(breakdown); + + this.panes.push(div); + + var callback = (function (id, domid) { + return (function (received) { + if (received[0]) + this.computeBreakdown(received[0], id, domid); + this.drawLegend(); + }).bind(this); + }).bind(this)(id, domid); + + var file = 'aggregate-' + id + '-' + this.machineId; + this.request([file], callback); + } + + this.lastRefresh = Date.now(); +} + +AWFY.isSubtest = function() { + if (this.view == 'overview') + return false; + if (this.view == 'breakdown') + return true; + if (this.view == 'single') { + if (this.subtest) + return true; + else + return false; + } + throw new Exception(); +} + +AWFY.requestRedraw = function () { + if (this.view == 'overview') { + this.request(['aggregate-' + this.machineId], + this.computeAggregate.bind(this)); + } else if (this.view == 'breakdown') { + var suite = AWFYMaster.suites[this.suiteName]; + var total = 0; + for (var i = 0; i < suite.tests.length; i++) { + var id = this.suiteName + '-' + suite.tests[i]; + var domid = id.replace(/ /g,'-').replace(/\./g, '-'); + var callback = (function (id, domid) { + return (function (received) { + if (received[0]) + this.computeBreakdown(received[0], id, domid); + if (++total == suite.tests.length) + this.drawLegend(); + }).bind(this); + }).bind(this)(id, domid); + var file = 'bk-aggregate-' + id + '-' + this.machineId; + this.request([file], callback); + } + } else if (this.view == 'single') { + var suite = AWFYMaster.suites[this.suiteName]; + var found = false; + for (var i = 0; i < suite.tests.length; i++) { + if (suite.tests[i] == this.subtest) { + found = true; + break; + } + } + if (found) { + var id = this.suiteName + '-' + this.subtest; + var domid = id.replace(/ /g,'-').replace(/\./g, '-'); + var callback = (function (id, domid) { + return (function (received) { + if (received[0]) + this.computeBreakdown(received[0], id, domid); + this.drawLegend(); + }).bind(this); + }).bind(this)(id, domid); + var file = 'bk-aggregate-' + id + '-' + this.machineId; + this.request([file], callback); + } else { + var id = this.suiteName; + var domid = id.replace(/ /g,'-').replace(/\./g, '-') + "-single"; + var callback = (function (id, domid) { + return (function (received) { + if (received[0]) + this.computeBreakdown(received[0], id, domid); + this.drawLegend(); + }).bind(this); + }).bind(this)(id, domid); + var file = 'aggregate-' + id + '-' + this.machineId; + this.request([file], callback); + } + } + this.lastRefresh = Date.now(); +} + +AWFY.changeMachine = function (machine_id) { + this.reset(this.view); + this.machineId = machine_id; + this.requestRedraw(); +} + +AWFY.reset = function (view) { + // Delete everything from the existing panes. + for (var i = 0; i < this.panes.length; i++) { + var elt = this.panes[i]; + var display = elt.data('awfy-display'); + if (!display) + continue; + display.shutdown(); + + // If we're changing views, remove the display. + if (this.view != view) { + elt.data('awfy-display', null); + elt.empty(); + } + } + + for (var mode in AWFYMaster.modes) + AWFYMaster.modes[mode].used = false; + + this.hasLegend = false; + this.aggregate = { }; + this.view = view; + + // Drop all outstanding XHR requests. + for (var i = 0; i < this.xhr.length; i++) + this.xhr[i].abort(); + this.xhr = []; +} + +AWFY.onHashChange = function () { + this.parseURL(); +} + +AWFY.refresh = function () { + if (Date.now() - this.lastRefresh >= this.refreshTime) + this.requestRedraw(); + window.setTimeout(this.refresh.bind(this), this.refreshTime * 1000); +} + +AWFY.parseURL = function () { + if (this.lastHash == window.location.hash) + return; + + var query = window.location.hash.substring(1); + var items = query.split('&'); + this.queryParams = {}; + for (var i = 0; i < items.length; i++) { + var item = items[i].split('='); + this.queryParams[item[0]] = item[1]; + } + + var machineId; + if ('machine' in this.queryParams) + machineId = parseInt(this.queryParams['machine']); + else + machineId = this.DEFAULT_MACHINE_ID; + + var view = this.queryParams['view']; + if (!view || (view != 'overview' && view != 'breakdown' && view != 'single')) + view = 'overview'; + if (view == 'breakdown' || view == 'single') { + var suiteName = this.queryParams['suite']; + if (!suiteName || !AWFYMaster.suites[suiteName]) + view = 'overview'; + } + var start = null; + var end = null; + if (view == 'single') { + var subtest = this.queryParams['subtest']; + var suite = AWFYMaster.suites[suiteName]; + var found = false; + for (var i = 0; i < suite.tests.length; i++) { + var test = suite.tests[i]; + if (subtest != test) + continue; + found = true; + break; + } + if (subtest && !found) { + view = 'breakdown'; + } else { + start = (this.queryParams['start'])?parseInt(this.queryParams['start']):null; + end = (this.queryParams['end'])?parseInt(this.queryParams['end']):null; + } + } + + // Make sure the menus are up to date. + if (this.view != 'none') { + if (this.machineId != machineId) { + this.updateMachineList(machineId); + this.updateSuiteList(machineId); + } + $('#breakdownlist .clicked').removeClass('clicked'); + if (view == 'overview') + $('#suite-overview').addClass('clicked'); + else if (view == 'breakdown') + $('#suite-' + suiteName).addClass('clicked'); + } + + if (view == this.view) { + // See if we just need to change the current machine. + if (view == 'overview') { + if (machineId != this.machineId) + this.changeMachine(machineId); + this.lastHash = window.location.hash; + return; + } else if (view == 'breakdown' || view == 'single') { + if (suiteName == this.suiteName) { + if (machineId != this.machineId) + this.changeMachine(machineId); + this.lastHash = window.location.hash; + return; + } + } + } + + // Nope. + this.machineId = machineId; + + if (view == 'overview') + this.showOverview(); + else if (view == 'breakdown') + this.showBreakdown(suiteName); + else if (view == 'single') + this.showSingle(suiteName, subtest, start, end); + + this.lastHash = window.location.hash; +} + +AWFY.updateMachineList = function (machineId) { + var menu = $('#machinelist'); + menu.empty(); + var showAll = false; + if (!AWFYMaster.machines[machineId].frontpage) + showAll = true; + for (var id in AWFYMaster.machines) { + var machine = AWFYMaster.machines[id]; + if (!showAll && !machine.frontpage) + continue; + var li = $('
  • '); + var a = $(''); + a.click((function (id) { + return (function (event) { + this.updateMachineList(parseInt(id)); + this.updateSuiteList(parseInt(id)); + this.changeMachine(parseInt(id)); + this.pushState(); + return false; + }).bind(this); + }).bind(this)(id)); + if (parseInt(id) == machineId) + a.addClass('clicked'); + if (!machine.recent_runs) + a.addClass('inactive'); + a.html(machine.description); + a.appendTo(li); + li.appendTo(menu); + } + $('#message').html(AWFYMaster.machines[machineId].message+"
     "); +} + +AWFY.updateSuiteList = function (machineId) { + var breakdown = $('#breakdownlist'); + breakdown.empty(); + + var home = $('').click( + (function (event) { + $('#breakdownlist .clicked').removeClass('clicked'); + $(event.target).addClass('clicked'); + this.showOverview(); + this.pushState(); + return false; + }).bind(this)) + .html('Overview') + .appendTo($('
  • ').appendTo(breakdown)); + if (this.view == 'overview') + home.addClass('clicked'); + + var suites = []; + for (var i=0; i < AWFYMaster.machines[machineId].suites.length; i++) { + var name = AWFYMaster.machines[machineId].suites[i]; + if (AWFYMaster.suites[name] && AWFYMaster.suites[name].visible == 1) + suites.push([name, AWFYMaster.suites[name]]); + } + + suites.sort(function (a, b) { + return a[1].sort_order - b[1].sort_order; + }); + + for (var i = 0; i < suites.length; i++) { + var name = suites[i][0]; + var suite = suites[i][1]; + var li = $('
  • '); + var a = $(''); + a.click((function (name) { + return (function(event) { + $('#breakdownlist .clicked').removeClass('clicked'); + $(event.target).addClass('clicked'); + this.showBreakdown(name); + this.pushState(); + return false; + }).bind(this); + }).bind(this)(name)); + if ((this.view == 'breakdown' || this.view == 'single') && this.suiteName == name) + a.addClass('clicked'); + a.html(suite.description); + a.appendTo(li); + li.appendTo(breakdown); + } +} + +AWFY.startup = function () { + this.panes = [$('#dromaeo-graph')]; + + this.parseURL(); + + // Add machine information to the menu. + var menu = $('#machinelist'); + this.updateMachineList(this.machineId); + + // Hide it by default. + $('#machinedrop').click((function (event) { + if (!menu.is(':visible') && !$('#about').is(':visible')) { + menu.show(); + } else { + menu.hide(); + } + return false; + }).bind(this)); + menu.hide(); + + // Add suite information to menu + var breakdown = $('#breakdownlist'); + this.updateSuiteList(this.machineId); + + // Hide it by default. + $('#bkdrop').click((function (event) { + if (!breakdown.is(':visible') && !$('#about').is(':visible')) { + breakdown.show(); + } else { + breakdown.hide(); + } + return false; + }).bind(this)); + breakdown.hide(); + + var about = $('#aboutdrop'); + about.click((function (event) { + var help = $('#about'); + if (!help.is(':visible')) { + $('.graph-container').hide(); + $('#breakdown').hide(); + help.show(); + about.text('Home'); + $('#breakdownhook').hide(); + $('#machinehook').hide(); + for (var i = 0; i < this.panes.length; i++) { + var elt = this.panes[i]; + var display = elt.data('awfy-display'); + if (display) + display.hideToolTips(); + } + } else { + help.hide(); + if (this.view == 'breakdown') + $('#breakdown').show(); + else + $('.graph-container').show(); + about.text('About'); + $('#breakdownhook').show(); + $('#machinehook').show(); + for (var i = 0; i < this.panes.length; i++) { + var elt = this.panes[i]; + var display = elt.data('awfy-display'); + if (display) + display.showToolTips(); + } + } + menu.hide(); + breakdown.hide(); + this.pushState(); + return false; + }).bind(this)); + + if (this.queryParams['help']) + about.trigger('click'); + + $(window).hashchange(this.onHashChange.bind(this)); + $(window).bind('popstate', this.onHashChange.bind(this)); + + window.setTimeout(this.refresh.bind(this), this.refreshTime * 1000); +} diff --git a/website/servo/index.html b/website/servo/index.html new file mode 100755 index 0000000..1859786 --- /dev/null +++ b/website/servo/index.html @@ -0,0 +1,153 @@ + + + + + + Servo Benchmarking + + + + + + + + + + + + + + + + + + + + + + +
    +
    +

    Servo-Benchmark

    +
    + + +
    +
    +
    + +
    +
    +
    + +
    +
    dromaeo time
    +

    Loading...

    +
    +
    +


    + +
    +
    +
    +
    +

    Benchmarking the performance of Mozilla Servo.

    + Basic usage: +
      +
    • The x-axis is the date we ran tests, and the y-axis is the score of the benchmark.
    • +
    • The front page displays a hybrid of historical data, as well as the most recent samples.
    • +
    • You can click on a datapoint to see a tooltip with more information.
    • +
    • Tooltips will either give you the revision range used to condense a point, or the changelog that occurred in between two points.
    • +
    • Tooltips can be dragged around, for easier comparison.
    • +
    • Some benchmarks use time (lower is better), and some use points (higher is better). We orient all graphs so lower is better, visually.
    • +
    • Use the "machine" menu to see different computers' benchmark results.
    • +
    • Use the "Breakdown" menu to drill down into individual benchmarks.
    • +
    • You can click and highlight any area of any graph to zoom in. It might pause to download data.
    • +
    • You can unzoom by double-clicking inside a graph.
    • +
    • A graph will refresh every 5 minutes if it is not zoomed in and has no attached tooltips.
    • +
    + FAQ: +
      +
    • +

      Who maintains this site?

      +

      This site is maintained by Mozilla-Servo team.

      +
    • +
    • +

      How does it work?

      +

      It is automated. Throughout the day, we checkout the latest source code to Mozilla Servo, and compile it. Then we run it through some benchmark suites, and tally up the scores into a database. This data gets exported as JSON which can then be easily plotted.

      +
    • +
    • +

      What are the graphs?

      +

      The graph currently shows Servo's performance on Dromaeo test suite.

      +
    • +
    • +

      What do the hover tips mean?

      +

      "Source" is where we got the engine from. "Tested" is when we downloaded the engine, compiled, and tested it. "Rev" is the unique point in the engine's revision history we tested. If the datapoint represents a range, there may be multiple revs. These numbers/strings are for developers to see which changes happened in between points in the graph.

      +
    • +
    • +

      Is this open source?

      +

      Fo' sho', github.com/dhananjay92/arewefastyet

      +

      (Adapted from github.com/h4writer/arewefastyet/)

      +
    • +
    • +

      Suggestions?

      +

      dhananjaynakrani@gmail.com

      +
    +
    +
    + + + + + + + + From 2aa745b45dc89eb1f4f34b97a6e01961869bac99 Mon Sep 17 00:00:00 2001 From: Dhananjay Nakrani Date: Thu, 22 Oct 2015 09:56:43 -0700 Subject: [PATCH 11/13] Fix github link issue [#9]. --- database/migration-1.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/database/migration-1.php b/database/migration-1.php index 07c3ce9..1f35e0e 100644 --- a/database/migration-1.php +++ b/database/migration-1.php @@ -3,7 +3,7 @@ function migrate() { // INSERT an entry into awfy_vendor mysql_query("INSERT INTO `awfy_vendor` (`name`, `vendor`, `csetURL`, `browser`, `rangeURL`) VALUES ('SpiderMonkey', - 'Mozilla', 'https://github.com/servo/servo/commits', 'Servo', + 'Mozilla', 'https://github.com/servo/servo/commits/', 'Servo', 'https://github.com/servo/servo/compare/{from}...{to}');" ) or die(mysql_error()); @@ -24,7 +24,7 @@ function rollback() { // Delete vendor now mysql_query("DELETE FROM `awfy_vendor` WHERE `awfy_vendor`.`name`='SpiderMonkey' AND - `awfy_vendor`.`vendor`='Mozilla' AND `awfy_vendor`.`csetURL`='https://github.com/servo/servo/commits' + `awfy_vendor`.`vendor`='Mozilla' AND `awfy_vendor`.`csetURL`='https://github.com/servo/servo/commits/' AND `awfy_vendor`.`browser`='Servo'; " ) or die(mysql_error()); } \ No newline at end of file From 2980f261ad54fdca800bd87caa12bbfe44d32c2d Mon Sep 17 00:00:00 2001 From: Dhananjay Nakrani Date: Thu, 22 Oct 2015 10:03:04 -0700 Subject: [PATCH 12/13] Copy frontpage.js for servo. --- website/servo/frontpage.js | 724 +++++++++++++++++++++++++++++++++++++ website/servo/index.html | 2 +- 2 files changed, 725 insertions(+), 1 deletion(-) create mode 100755 website/servo/frontpage.js diff --git a/website/servo/frontpage.js b/website/servo/frontpage.js new file mode 100755 index 0000000..fbbd166 --- /dev/null +++ b/website/servo/frontpage.js @@ -0,0 +1,724 @@ +// vim: set ts=4 sw=4 tw=99 et: +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +function Display(awfy, id, domid, elt) +{ + this.awfy = awfy; + this.id = id; + this.domid = domid; + this.elt = elt; + this.attachedTips = []; + this.plot = null; + this.hovering = null; + this.graph = null; + this.orig_graph = null; +} + +Display.prototype.setGraph = function (graph) +{ + // We keep both the original dataset and the one we send and display to + // flot, so we can redraw and hide lines. In the future this should be + // tightened up, so that code working with the display graph uses "graph" + // and code (if any) working with the original data set uses "orig_graph". + // And really no code should be accessing "graph" until draw(). + this.orig_graph = graph; + this.graph = graph; +} + +Display.prototype.setup = function (graph) +{ + this.setGraph(graph); + + this.selectDelay = null; + + if (graph.aggregate) + this.setHistoricalMidpoint(); + else + this.aggregate = -1; + + this.zoomInfo = { prev: null, + level: 'aggregate' + }; + + this.elt.bind("plothover", this.onHover.bind(this)); + this.elt.bind('plotclick', this.onClick.bind(this)); + this.elt.bind('plotselected', this.plotSelected.bind(this)); + this.elt.bind('dblclick', (function () { + if (this.zoomInfo.level != 'aggregate') + this.unzoom(); + }).bind(this)); +} + +Display.MaxPoints = 50; +Display.Months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; + +Display.prototype.shutdown = function () { + this.elt.unbind('plothover'); + this.elt.unbind('plotclick'); + this.elt.unbind('plotselected'); + this.elt.unbind('dblclick'); + if (this.hovering) { + this.hovering.remove(); + this.hovering = null; + } + this.detachTips(); + this.plot = null; + this.setGraph(null); +} + +Display.prototype.shouldRefresh = function () { + if (this.graph) { + for (var i = 0; i < this.attachedTips.length; i++) { + var tooltip = this.attachedTips[i]; + if (tooltip.attached()) + return false; + } + if (this.zoomInfo.level != 'aggregate') + return false; + this.shutdown(); + } + return true; +} + +Display.prototype.setHistoricalMidpoint = function () { + // Find the range of historical points. + for (var i = 0; i < this.graph.timelist.length; i++) { + if (this.graph.timelist[i] >= this.graph.earliest) + break; + } + + if (i && i != this.graph.timelist.length) + this.historical = i; +} + +// Copy flot's tick algorithm. +Display.prototype.tickSize = function (min, max) { + var noTicks = 0.3 * Math.sqrt(this.elt.width()); + var delta = (max - min) / noTicks; + var dec = -Math.floor(Math.log(delta) / Math.LN10); + var magn = Math.pow(10, -dec); + var norm = delta / magn; + var size; + if (norm < 1.5) { + size = 1; + } else if (norm < 3) { + size = 2; + if (norm > 2.25) { + size = 2.5; + ++dec; + } + } else if (norm < 7.5) { + size = 5; + } else { + size = 10; + } + + size *= magn; + return size; +}; + +Display.prototype.aggregateTicks = function () { + // Draw historical ticks at a higher density. + var ticks = this.tickSize(0, this.graph.timelist.length); + var list = []; + + // This is all a bunch of hardcoded hacks for now. + var preticks, preticklist; + + if (ticks == 5) { + preticks = 6; + preticklist = [2, 4]; + } else if (ticks == 10) { + preticks = 9; + preticklist = [3, 6]; + } else { + preticks = ticks; + } + + var last_year = undefined; + var current_year = (new Date()).getFullYear(); + for (var i = 0; i < this.historical; i += preticks) { + var d = new Date(this.graph.timelist[i] * 1000); + var text = Display.Months[d.getMonth()]; + + // Some graphs span over a year, so add in a hint when the year + // changes. + if ((i == 0 && d.getFullYear() != current_year) || + (last_year && d.getFullYear() != last_year)) + { + text += " " + d.getFullYear(); + last_year = d.getFullYear(); + } + + // Add the tick mark, then try to add some more empty ones to + // make the graph appear denser. + list.push([i, text]); + if (preticklist) { + for (var j = 0; j < preticklist.length; j++) { + var v = i + preticklist[j]; + if (v >= this.historical) + break; + list.push([v, ""]); + } + } + } + + // Figure out where we should start placing sparser lines, since + // we don't want them too close to the historical lines. + i = list[list.length - 1][0] + ticks; + + // If the aggregate graph has both historical and recent points, + for (; i < this.graph.timelist.length; i += ticks) { + var d = new Date(this.graph.timelist[Math.floor(i)] * 1000); + var text = Display.Months[d.getMonth()] + " " + d.getDate(); + list.push([i, text]); + } + + return list; +} + +Display.prototype.draw = function () { + var options = { }; + + // We always start out by using the original graph, since we may modify + // this one locally. Start by stripping out any lines that should be + // hidden. + var new_info = []; + var new_lines = []; + for (var i = 0; i < this.orig_graph.info.length; i++) { + var info = this.orig_graph.info[i]; + var mode = AWFYMaster.modes[info.modeid]; + if (!mode) + continue; + + // Strip JM+TI, BC + if (info.modeid == 12 || info.modeid == 15) + continue; + + mode.used = true; + if (mode.hidden) + continue; + + new_info.push(info); + new_lines.push(this.orig_graph.lines[i]); + } + this.graph = { + lines: new_lines, + info: new_info, + timelist: this.orig_graph.timelist, + earliest: this.orig_graph.earliest, + aggregate: this.orig_graph.aggregate, + direction: this.orig_graph.direction + }; + + options.lines = { show: true }; + options.points = { fillColor: "#ffffff", show: true }; + options.borderWidth = 1.5; + options.borderColor = "#BEBEBE"; + options.legend = { show: false }; + options.xaxis = { }; + options.yaxis = { }; + options.grid = { hoverable: true, clickable: true }; + options.selection = { mode: 'x' } + + // Aggregate view starts from 0. We space things out when zooming in. + if (this.graph.aggregate && this.awfy.type != 'overview') + options.yaxis.min = 0; + + if (this.graph.direction == 1) { + options.yaxis.transform = function (v) { + return -v; + }; + options.yaxis.inverseTransform = function (v) { + return -v; + }; + } + + if (this.graph.aggregate && this.historical) { + // If the graph has both historical and recent points, indicated by + // the "historical" midpoint, then we change some graph formatting + // to reflect that part of the graph has a greater time density. + // + // To make this work, we modified flot to pass in its plot variable + // when invoking this callback, in order to use c2p(). + options.points.symbol = (function (ctx, x, y, radius, shadow, plot) { + var axis = plot.getAxes(); + var rx = Math.round(axis.xaxis.c2p(x)); + if (this.graph.timelist[rx] < this.graph.earliest) { + ctx.strokeRect(x - radius / 3, y - radius / 3, radius * 2/3, radius * 2/3); + // Disable clearRect due to bug in Firefox for Android (bug 936177) + //ctx.clearRect(x - radius / 4, y - radius / 4, radius / 2, radius / 2); + } else { + ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false); + } + }).bind(this); + + options.xaxis.ticks = this.aggregateTicks(); + options.xaxis.transform = (function (v) { + if (v < 30) + return v; + var total = this.graph.timelist.length - 30; + return 30 + (v - 30)/total * 30; + }).bind(this); + options.xaxis.inverseTransform = (function (v) { + if (v < 30) + return v; + var total = this.graph.timelist.length - 30; + return 30 + (v - 30)/30 * total; + }).bind(this); + } + + options.yaxis.tickFormatter = function (v, axis) { + if (Math.round(v) != v) + return v.toFixed(2); + return v; + } + + if (!options.xaxis.ticks) { + options.xaxis.tickFormatter = (function (v, axis) { + v = Math.round(v); + if (v < 0 || v >= this.graph.timelist.length) + return ''; + var t = this.graph.timelist[v]; + var d = new Date(t * 1000); + return Display.Months[d.getMonth()] + " " + d.getDate(); + }).bind(this); + } + + this.plot = $.plot(this.elt, this.graph.lines, options); + + if (this.graph.direction == 1) { + var yaxisLabel = $("
    ") + .text("Score") + .appendTo(this.elt); + } else { + var yaxisLabel = $("
    ") + .text("Execution Time (ms)") + .appendTo(this.elt); + } + yaxisLabel.css("margin-top", yaxisLabel.width() / 2 - 20); +} + +Display.prototype.plotSelected = function (event, ranges) { + this.selectDelay = new Date(); + + var from_x = Math.floor(ranges.xaxis.from); + if (from_x == ranges.xaxis.from) + from_x -= 1; + if (from_x < 0) + from_x = 0; + + var to_x = Math.ceil(ranges.xaxis.to); + if (to_x == ranges.xaxis.to) + to_x += 1; + if (to_x >= this.graph.timelist.length) + to_x = this.graph.timelist.length - 1; + + var start = this.graph.timelist[from_x]; + var end = this.graph.timelist[to_x]; + + AWFY.trackZoom(start, end); + + var prev = this.zoomInfo.prev; + if (prev && this.zoomInfo.level == 'month') { + // Estimate the number of datapoints we had in the old range. + var oldstart = AWFY.findX(prev, this.graph.timelist[0]); + var oldend = AWFY.findX(prev, this.graph.timelist[this.graph.timelist.length - 1]); + + // Estimate the number of datapoints we'd have in the new range. + var newstart = AWFY.findX(prev, start); + var newend = AWFY.findX(prev, end); + + // Some heuristics to figure out whether we should fetch more data. + var zoom = (newend - newstart) / (oldend - oldstart); + if ((zoom >= 0.8 && (newend - newstart >= Display.MaxPoints * 1.5)) || + (newend - newstart >= Display.MaxPoints * 5)) + { + // Okay! Trim the cached graph, then display. + var graph = AWFY.trim(prev, newstart, newend); + this.localZoom(graph); + return; + } + } + + // If we already have the highest level of data, jump right in. + if (prev && this.zoomInfo.level == 'raw') { + var oldstart = AWFY.findX(prev, this.graph.timelist[0]); + var oldend = AWFY.findX(prev, this.graph.timelist[this.graph.timelist.length - 1]); + + this.plot.clearSelection(); + + // If we can't really zoom in any more, don't bother. + if (oldend - oldstart < Display.MaxPoints / 2) + return; + + // Require at least a few datapoints. + var newstart = AWFY.findX(prev, start); + var newend = AWFY.findX(prev, end); + if (oldend - oldstart <= 3) + return; + + var graph = AWFY.trim(prev, newstart, newend); + this.localZoom(graph); + return; + } + + // Disable further selections since we wait for the XHR to go through. + this.plot.disableSelection(); + + // Clear the cached graph, since we'll get a new one. + this.zoomInfo.prev = null; + + if (this.zoomInfo.level == 'aggregate') { + this.awfy.requestZoom(this, 'condensed', start, end); + this.zoomInfo.level = 'month'; + } else { + this.awfy.requestZoom(this, 'raw', start, end); + this.zoomInfo.level = 'raw'; + } +} + +Display.prototype.localZoom = function (graph) { + graph = AWFY.condense(graph, Display.MaxPoints); + this.setGraph(graph); + this.draw(); + this.plot.enableSelection(); + this.plot.clearSelection(); + this.detachTips(); +} + +Display.prototype.completeZoom = function (graph, start, end) { + // Copy properties from the old graph before resetting. + graph.direction = this.graph.direction; + + // Cache the original graph in case it's dense enough to zoom in more + // without fetching more points via XHR. + if (!this.zoomInfo.prev) + this.zoomInfo.prev = graph; + + var first = AWFY.findX(graph, start); + var last = AWFY.findX(graph, end); + graph = AWFY.trim(graph, first, last); + + // If we got a paltry number of datapoints, skip this and zoom in more. + if (this.zoomInfo.level == 'month' && graph.timelist.length < Display.MaxPoints / 2) { + this.zoomInfo.prev = null; + this.awfy.requestZoom(this, 'raw', start, end); + this.zoomInfo.level = 'raw'; + return; + } + + this.localZoom(graph); +} + +Display.prototype.cancelZoom = function () { + this.plot.enableSelection(); + this.plot.clearSelection(); + + // Reset the zoom level we think we have. + if (!this.zoomInfo.prev) { + if (this.zoomInfo.level == 'raw') + this.zoomInfo.level = 'month'; + else if (this.zoomInfo.level == 'month') + this.zoomInfo.level = 'aggregate'; + } +} + +Display.prototype.unzoom = function () { + this.setGraph(AWFY.aggregate[this.id]); + this.setHistoricalMidpoint(); + this.draw(); + this.plot.enableSelection(); + this.plot.clearSelection(); + this.detachTips(); + this.zoomInfo.level = 'aggregate'; + + AWFY.trackZoom(null, null); +} + +Display.prototype.detachTips = function () { + for (var i = 0; i < this.attachedTips.length; i++) + this.attachedTips[i].detach(); + this.attachedTips = []; +} + +Display.prototype.createToolTip = function (item, extended) { + var so = extended ? '' : ''; + var sc = extended ? '' : ''; + + // Figure out the line this corresponds to. + var line = this.graph.info[item.seriesIndex]; + if (!line) + return; + + var text = ""; + var x = item.datapoint[0]; + var y = item.datapoint[1]; + + // Show suite version. + if (line.data[x][3]) { + var suiteVersion = AWFYMaster.suiteversions[line.data[x][3]]["name"]; + text += so + 'suite: ' + sc + suiteVersion + '
    '; + } + + // Show score. + if (this.graph.direction == -1) + text += so + 'score: ' + sc + y.toFixed(2) + 'ms
    '; + else + text += so + 'score: ' + sc + y.toFixed() + '
    '; + + // Find the point previous to this one. + var prev = null; + for (var i = x - 1; i >= 0; i--) { + if (line.data[i] && line.data[i][0]) { + prev = line.data[i]; + break; + } + } + + if (prev) { + // Compute a difference. + var diff = Math.round((y - prev[0]) * 10) / 10; + var perc = -Math.round(((y - prev[0]) / prev[0]) * 1000) / 10; + var better; + if ((perc < 0 && this.graph.direction == -1) || + (perc > 0 && this.graph.direction == 1)) + { + better = 'worse'; + } else { + better = 'better'; + } + perc = Math.abs(perc); + + if (diff === diff) { + if (extended) + text += so + 'delta' + sc + ': ' + diff; + else + text += String.fromCharCode(916) + ': ' + diff; + if (this.graph.direction == -1) + text += 'ms'; + text += ' (' + perc + '% ' + better + ')
    '; + } + } + + // Find the vendor. + var mode = AWFYMaster.modes[line.modeid]; + var vendor; + if (mode) + vendor = AWFYMaster.vendors[mode.vendor_id]; + if (vendor) { + text += so + 'source: ' + sc + + vendor.browser + + ' (' + mode.name + ')'+ + '
    '; + } + + // Find the datapoint. + var point = line.data[x]; + + if (extended) { + if (point.length > 1 && point[2] && point[1] != point[2]) { + if (vendor.rangeURL) { + var url = vendor.rangeURL + .replace("{from}", point[1]) + .replace("{to}", point[2]); + text += so + 'revs: ' + sc + + '' + point[1] + " to " + point[2] + ''; + } else { + text += so + 'revs: ' + sc + + '' + point[1] + '' + + ' to ' + + '' + point[2] + ''; + } + } else { + text += so + 'rev: ' + sc + + '' + point[1] + ''; + + if (prev && vendor.rangeURL) { + var url = vendor.rangeURL + .replace("{from}", prev[1]) + .replace("{to}", point[1]) + .replace("{num}", point[1] - prev[1]); + text += ' (changelog)'; + } + } + text += '
    '; + } else { + if (point.length > 1 && point[2] && point[1] != point[2]) { + text += so + 'revs: ' + sc + + point[1] + + ' to ' + + point[2] + + '
    '; + } else { + text += so + 'rev: ' + sc + point[1] + '
    '; + } + } + + var pad = function (d) { + if (d == 0) + return '00'; + else if (d < 10) + return '0' + d; + else + return '' + d; + } + + // Format a year, if we should. + if (extended) { + var current_year = (new Date()).getFullYear(); + var datefmt = function (t, forceYear, omitTime) { + var d = new Date(t * 1000); + var text = Display.Months[d.getMonth()] + ' ' + d.getDate(); + if (d.getFullYear() != current_year || forceYear) + text += ', ' + d.getFullYear(); + if (!omitTime && (d.getHours() || d.getMinutes())) { + text += ' '; + text += pad(d.getHours()) + ':' + + pad(d.getMinutes()); + } + return text; + } + + if (point.length > 1 && + point[2] && + point[1] != point[2] && + x < this.graph.timelist.length - 1) + { + text += so + 'tested: ' + sc + + datefmt(this.graph.timelist[x], false, true) + ' to ' + + datefmt(this.graph.timelist[x + 1], true, true) + '
    '; + } else { + text += so + 'tested: ' + sc + + datefmt(this.graph.timelist[x], false, false) + '
    '; + } + } else { + // Include a short timestamp if we're looking at recent changesets. + var d = new Date(this.graph.timelist[x] * 1000); + var now = new Date(); + text += so + 'tested: ' + sc; + if (this.graph.aggregate && x < this.historical) + text += 'around '; + text += Display.Months[d.getMonth()] + ' ' + d.getDate(); + if (now.getFullYear() != d.getFullYear()) + text += ', ' + d.getFullYear() + ' '; + else + text += ' '; + if (!this.graph.aggregate || x >= this.historical) + text += pad(d.getHours()) + ':' + pad(d.getMinutes()) + '
    '; + } + + if (extended && point.length >= 5) { + if (!point[4]) { + text += so + 'regression: ' + sc + "zoom in to view regression info." + '
    '; + } else { + var id = Math.random(); + while (document.getElementById("update_"+id)) + id = Math.random(); + + text += so + 'regression?: ' + sc + "" + '
    '; + text += so + 'extra info: ' + sc + "" + '
    '; + var subtest = AWFY.isSubtest(); + $.get("data-info.php", { + subtest: subtest, + id: point[4] + }, function(data) { + var html = data.regression.status; + if (data.regression.id) { + html += " (view)" + } else { + if (subtest) + html += " (report regression)" + else + html += " (report regression)" + } + document.getElementById("update_regression_"+id).innerHTML = html; + document.getElementById("update_info_"+id).innerHTML = data.info; + }, "json"); + } + } + + return new ToolTip(item.pageX, item.pageY, item, text); +} + +Display.prototype.onClick = function (event, pos, item) { + // Remove the existing hovertip. + if (this.hovering) { + this.hovering.remove(); + this.hovering = null; + } + + if (!item) + return; + + if (this.selectDelay) { + // When unselecting a plot, the cursor might be over a point, which + // will give us annoying extra tooltips. To combat this, we force a + // small delay. + var d = new Date(); + if (d - this.selectDelay <= 1000) { + this.plot.unhighlight(); + return; + } + } + + var tooltip = this.createToolTip(item, true); + tooltip.drawFloating(); + + this.lastToolTip = tooltip; + + // The color of the line will be the series color. + var line = this.graph.info[item.seriesIndex]; + if (!line) + return; + var mode = AWFYMaster.modes[line.modeid]; + if (!mode) + return; + + tooltip.attachLine(mode.color); + this.attachedTips.push(tooltip); +} + +Display.prototype.areItemsEqual = function (item1, item2) { + return item1.seriesIndex == item2.seriesIndex && + item1.dataIndex == item2.dataIndex && + item1.datapoint[0] == item2.datapoint[0]; +} + +Display.prototype.onHover = function (event, pos, item) { + // Are we already hovering over something? + if (this.hovering) { + // If we're hovering over the same point, don't do anything. + if (item && this.areItemsEqual(item, this.hovering.item)) + return; + + // Otherwise, remove the div since we will redraw. + this.hovering.remove(); + this.hovering = null; + } + + // If we have a pinned tooltip that has not been moved yet, don't draw a + // second tooltip on top of it. + if (this.lastToolTip && !this.lastToolTip.dragged && !this.lastToolTip.closed) + return; + + if (!item) + return; + + this.hovering = this.createToolTip(item, false); + this.hovering.drawBasic(); +} + +Display.prototype.hideToolTips = function () { + for (var i = 0; i < this.attachedTips.length; i++) + this.attachedTips[i].hide(); +} + +Display.prototype.showToolTips = function () { + for (var i = 0; i < this.attachedTips.length; i++) + this.attachedTips[i].show(); +} + diff --git a/website/servo/index.html b/website/servo/index.html index 1859786..06d9fa7 100755 --- a/website/servo/index.html +++ b/website/servo/index.html @@ -13,7 +13,7 @@ - + From 8662ee6c3bf435827f3ab81ff477b3be3bc32644 Mon Sep 17 00:00:00 2001 From: Dhananjay Nakrani Date: Thu, 22 Oct 2015 10:04:35 -0700 Subject: [PATCH 13/13] Stop showing regression links in tooltip for servo-awfy. --- website/servo/frontpage.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/website/servo/frontpage.js b/website/servo/frontpage.js index fbbd166..3060f6e 100755 --- a/website/servo/frontpage.js +++ b/website/servo/frontpage.js @@ -611,6 +611,8 @@ Display.prototype.createToolTip = function (item, extended) { text += pad(d.getHours()) + ':' + pad(d.getMinutes()) + '
    '; } + /* + // Regression doesn't work for servo-awfy yet! Skip it. if (extended && point.length >= 5) { if (!point[4]) { text += so + 'regression: ' + sc + "zoom in to view regression info." + '
    '; @@ -640,6 +642,7 @@ Display.prototype.createToolTip = function (item, extended) { }, "json"); } } + */ return new ToolTip(item.pageX, item.pageY, item, text); }