summaryrefslogtreecommitdiff
path: root/timings/graph.html
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2009-12-18 08:03:50 +0000
committerSteven Knight <knight@baldmt.com>2009-12-18 08:03:50 +0000
commitea3efaf5c76922329aae72a0ae5613df7940f59a (patch)
tree6e1f3e3dd9a03cf38faa62500cd224b751d27905 /timings/graph.html
parentf95d845fd451f6a0c1e3a6f1c51156250703b6ae (diff)
downloadscons-ea3efaf5c76922329aae72a0ae5613df7940f59a.tar.gz
Move the timings-specific pieces of the buildbot infrastructure into
the trunk/timings directory. We'll map them into the buildbot directory using svn:externals. This will let us keep all the pieces of a timing configuration, including its buildbot pieces, in one place, and will let us simplify the Master initialization (since it will be able to look on-disk for the configurations for which it should set up buildbot steps, instead of querying the SVN server).
Diffstat (limited to 'timings/graph.html')
-rw-r--r--timings/graph.html411
1 files changed, 411 insertions, 0 deletions
diff --git a/timings/graph.html b/timings/graph.html
new file mode 100644
index 00000000..e418069d
--- /dev/null
+++ b/timings/graph.html
@@ -0,0 +1,411 @@
+<html>
+
+<!--
+ Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+-->
+
+<!--
+ A brief note on terminology as used here: a "graph" is a plotted screenful
+ of data, showing the results of one type of test: for example, the
+ page-load-time graph. A "trace" is a single line on a graph, showing one
+ one for the test: for example, the reference build trace on the
+ page-load-time graph.
+
+ This page plots arbitrary numerical data loaded from files in a specific
+ format. It uses two or more data files, all JSON-encoded:
+
+ graphs.dat: a list of objects, each with these properties: name (the name
+ of a graph) and units (the units for the data to be read by humans).
+ Schematically:
+ [{"name": <graph_name>, "units": <units>}, ...]
+
+ <graphname>-summary.dat: for each of the graphs listed in graphs.dat, the
+ corresponding summary file holds rows of data. Each row of data is an
+ object with several properties:
+ "rev": the revision number for this row of data
+ "traces": an object with several properties of its own. The name of
+ the property corresponds to a trace name, used only as an
+ internal identifier, and the property's value is an array of
+ its measurement and that measurement's standard deviation (or
+ other measurement error).
+ Schematically:
+ {"rev": <rev>,
+ "traces": {<trace_name1>: [<value1>, <stddev1>],
+ <trace_name2>: [<value2>, <stddev2>], ...}
+ }
+-->
+<head>
+<style>
+body {
+ font-family: sans-serif;
+}
+div#output {
+ cursor: pointer;
+}
+div#switcher {
+ cursor: pointer;
+}
+div#switcher a {
+ border-top: 1px solid black;
+ border-left: 1px solid black;
+ padding-left: 0.5em;
+ padding-right: 0.5em;
+}
+canvas.plot {
+ border: 1px solid black;
+}
+div.plot-coordinates {
+ font-family: monospace;
+}
+iframe {
+ display: none;
+ width: 100%;
+ height: 100%;
+ border: none;
+}
+div.selector {
+ border: solid 1px black;
+ cursor: pointer;
+ padding-left: 0.3em;
+ background-color: white;
+}
+div.selector:hover {
+ background-color: rgb(200,200,250);
+}
+div.selected {
+ border-left: none;
+}
+div#selectors {
+ width: 80px;
+ display: none;
+}
+#explain {
+ font-size: 0.75em;
+ font-style: italic;
+ color: rgb(100,100,100);
+}
+</style>
+
+<script src="js/common.js"></script>
+<script src="js/plotter.js"></script>
+<script src="js/coordinates.js"></script>
+<!-- <script src="config.js"></script> -->
+<script>
+var Config = {
+ 'title': "TODO title",
+ 'source': "http://scons.tigris.org/svn/scons/trunk",
+ 'changeLinkPrefix': "changelog.html?mode=html&range=",
+ 'builder': "TODO ",
+ 'builderLink': "http://buildbot.scons.org:8010/",
+ 'detailTabs': ['view-change'],
+};
+document.title = Config.title + ' - ' + Config.buildslave;
+
+var did_position_details = false;
+var units = 'thing-a-ma-bobs';
+var graph_list = [];
+var first_trace = '';
+
+var params = ParseParams();
+if (!('history' in params)) {
+ params.history = 150;
+ // make this option somewhat user discoverable :-/
+ window.location.href = MakeURL(params);
+}
+
+function jsonToJs(data) {
+ return eval('(' + data + ')')
+}
+
+function report_error(error) {
+ document.getElementById("output").innerHTML = "<p>" + error + "</p>";
+}
+
+function received_graph_list(data, error) {
+ if (error) {
+ report_error(error);
+ return;
+ }
+ graph_list = jsonToJs(data);
+
+ if (!('graph' in params) || params.graph == '') {
+ if (graph_list.length > 0)
+ params.graph = graph_list[0].name
+ }
+
+ // Add a selection tab for each graph, and find the units for the selected
+ // one while we're at it.
+ tabs = [];
+ for (var index = 0; index < graph_list.length; ++index) {
+ var graph = graph_list[index];
+ tabs.push(graph.name);
+ if (graph.name == params.graph)
+ units = graph.units;
+ }
+ initPlotSwitcher(tabs);
+
+ // Fetch the data for the selected graph.
+ fetch_summary();
+}
+
+function go_to(graph) {
+ params.graph = graph;
+ if (params.graph == '')
+ delete params.graph;
+ window.location.href = MakeURL(params);
+}
+
+function get_url() {
+ new_url = window.location.href;
+ new_url = new_url.replace(/50/, "150");
+ new_url = new_url.replace(/\&lookout/, "");
+ return new_url;
+}
+
+function on_clicked_plot(prev_cl, cl) {
+ if ('lookout' in params) {
+ window.open(get_url());
+ return;
+ }
+
+ // Define sources for detail tabs
+ if ('view-change' in Config.detailTabs) {
+ document.getElementById('view-change').
+ setAttribute('src', Config.changeLinkPrefix + prev_cl + ':' + cl);
+ }
+ if ('view-pages' in Config.detailTabs) {
+ document.getElementById('view-pages').
+ setAttribute('src', 'details.html?cl=' + cl + '&trace=' + first_trace);
+ }
+ if ('view-coverage' in Config.detailTabs) {
+ document.getElementById('view-coverage').
+ setAttribute('src', Config.coverageLinkPrefix + cl);
+ }
+
+ if (!did_position_details) {
+ position_details();
+ did_position_details = true;
+ }
+}
+
+function received_summary(data, error) {
+ if (error) {
+ report_error(error);
+ return;
+ }
+ // Parse the summary data file.
+ var rows = data.split('\n');
+ var max_rows = rows.length;
+ if (max_rows > params.history)
+ max_rows = params.history;
+
+ var allTraces = {};
+
+ // graphData[rev] = {trace1:[value, stddev], trace2:[value, stddev], ...}
+ var graphData = {};
+ for (var i = 0; i < max_rows; ++i) {
+ if (!rows[i].length)
+ continue;
+ var row = jsonToJs(rows[i]);
+ var traces = row['traces'];
+ var revision = parseInt(row['rev']);
+ graphData[revision] = traces;
+
+ // Collect unique trace names.
+ for (var traceName in traces)
+ allTraces[traceName] = 1;
+ }
+
+ // Build a list of all the trace names we've seen, in the order in which
+ // they appear in the data file. Although JS objects are not required by
+ // the spec to iterate their properties in order, in practice they do,
+ // because it causes compatibility problems otherwise.
+ var traceNames = [];
+ for (var traceName in allTraces)
+ traceNames.push(traceName);
+
+ first_trace = traceNames[0];
+
+ // Build and numerically sort a list of revision numbers.
+ var revisionNumbers = [];
+ for (var rev in graphData)
+ revisionNumbers.push(rev);
+ revisionNumbers.sort(
+ function(a, b) { return parseInt(a, 10) - parseInt(b, 10) });
+
+ // Build separate ordered lists of trace data.
+ var traceData = {};
+ for (var revIndex = 0; revIndex < revisionNumbers.length; ++revIndex) {
+ var rev = revisionNumbers[revIndex];
+ var revisionData = graphData[rev];
+ for (var nameIndex = 0; nameIndex < traceNames.length; ++nameIndex) {
+ var traceName = traceNames[nameIndex];
+ if (!traceData[traceName])
+ traceData[traceName] = [];
+ if (!revisionData[traceName])
+ traceData[traceName].push([NaN, NaN]);
+ else
+ traceData[traceName].push(revisionData[traceName]);
+ }
+ }
+ var plotData = [];
+ for (var traceName in traceData)
+ plotData.push(traceData[traceName]);
+
+ var plotter = new Plotter(revisionNumbers, plotData, traceNames, units,
+ document.getElementById("output"), true);
+ plotter.onclick = on_clicked_plot;
+ plotter.plot();
+}
+
+function fetch_summary() {
+ if ('graph' in params)
+ file = escape(params.graph) + ".dat"
+ else
+ file = "summary.dat"
+ Fetch(file, received_summary);
+}
+
+function fetch_graph_list() {
+ Fetch("graphs.dat", received_graph_list);
+}
+
+function initPlotSwitcher(tabs) {
+ var switcher = document.getElementById("switcher");
+ for(var i = 0; i < tabs.length; i++) {
+ var anchor = document.createElement("a");
+ anchor.appendChild(document.createTextNode(tabs[i] + " "));
+ anchor.addEventListener("click", goToClosure(tabs[i]), false);
+ switcher.appendChild(anchor);
+ }
+}
+
+function goToClosure(graph) {
+ return function(){go_to(graph)};
+}
+
+function position_details() {
+ var output = document.getElementById("output");
+
+ var win_height = window.innerHeight;
+
+ var details = document.getElementById("views");
+
+ var views = document.getElementById("views");
+ var selectors = document.getElementById("selectors");
+ selectors.style.display = "block";
+
+ var views_width = output.offsetWidth - selectors.offsetWidth;
+
+ views.style.border = "1px solid black";
+ views.style.width = views_width + "px";
+ views.style.height = (win_height - output.offsetHeight - output.offsetTop -
+ 30) + "px";
+
+ selectors.style.position = "absolute";
+ selectors.style.left = (views.offsetLeft + views_width + 1) + "px";
+ selectors.style.top = views.offsetTop + "px";
+
+ // Change to the first detail tab
+ for (var tab in Config.detailTabs) {
+ change_view(tab);
+ break;
+ }
+}
+
+function change_view(target) {
+ for (var tab in Config.detailTabs) {
+ document.getElementById(tab).style.display =
+ (tab == target ? "block" : "none");
+ }
+}
+
+function init() {
+ // We need to fill the graph list before parsing the params or fetching the
+ // data, so we have a default graph in case none was specified.
+ fetch_graph_list();
+}
+
+window.addEventListener("load", init, false);
+</script>
+</head>
+
+
+<body>
+<div id="header_lookout" align="center">
+ <font style='color: #0066FF; font-family: Arial, serif;
+ font-size: 20pt; font-weight: bold;'>
+ <script>
+ document.write("<a target=\"_blank\" href=\"");
+ document.write(get_url());
+ document.write("\">");
+ if ('header' in params && params.header != '') {
+ document.write(escape(params.header));
+ } else {
+ document.write(Config.title);
+ }
+ document.write("</a>");
+ </script>
+ </font>
+</div>
+
+<div id="header_text">
+Builds generated by the <a href="http://buildbot.chromium.org/">buildbot</a>
+are run through <b>
+<script>
+document.write(Config.title);
+</script>
+</b>and the results of that test are charted here.
+</div>
+
+<div id="explain">
+The vertical axis is measured values, and the horizontal
+axis is the revision number for the build being tested.
+</div>
+<p></p>
+<div id="switcher">
+
+</div>
+<div id="output"></div>
+<div id="details">
+ <div id="views">
+ <script>
+ for (var tab in Config.detailTabs) {
+ document.write("<iframe id=\"" + tab + "\"></iframe>");
+ }
+ </script>
+ </div>
+ <div id="selectors">
+ <script>
+ var firstTab = true;
+ for (var tab in Config.detailTabs) {
+ document.write("<div ");
+ if (firstTab) {
+ firstTab = false;
+ } else {
+ document.write("style=\"border-top: none\" ");
+ }
+ document.write("class=\"selector\" onclick=\"change_view('"
+ + tab + "')\">" + Config.detailTabs[tab] + "</div>");
+ }
+ </script>
+ </div>
+</div>
+<pre id="log"></pre>
+<script>
+if ('lookout' in params) {
+ document.getElementById("switcher").style.display = "none";
+ document.getElementById("details").style.display = "none";
+ document.getElementById("header_text").style.display = "none";
+ document.getElementById("explain").style.display = "none";
+ if ('thumbnail' in params) {
+ document.getElementById("header_lookout").style.display = "none";
+ }
+} else {
+ document.getElementById("header_lookout").style.display = "none";
+}
+</script>
+</body>
+</html>