scons_gd/scons/timings/graph.html
2022-10-15 16:06:26 +02:00

414 lines
11 KiB
HTML

<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>
Config.source = "http://scons.tigris.org/svn/scons/trunk";
Config.changeLinkPrefix = "changelog.html?mode=html&range=";
Config.builder = "TODO";
Config.buildbotLink = "http://buildbot.scons.org:8010/";
Config.detailTabs = {'view-change': 'CL'};
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();
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(/\?lookout/, "?");
new_url = new_url.replace(/\&thumbnail/, "");
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').
// TODO: The tigris.org source browser only lets us pull up
// one revision. That's okay for our current behavior of
// timing each revision separately, but if we go back to merging
// build requests from multiple revisions, we'll need an
// intermediary CGI script.
//setAttribute('src', Config.changeLinkPrefix + prev_cl + ':' + cl);
setAttribute('src',
'http://scons.tigris.org/source/browse/scons?view=rev&revision=' + 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 ('history' in params && max_rows > params.history) {
max_rows = params.history;
} else if ('lookout' in params && max_rows > 150) {
max_rows = 150;
}
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">
<script>
document.write('<a href="' + Config.buildbotLink + '">SCons buildbot</a>' +
' timings for the <b>' + Config.title + '</b> configuration.')
if ('graph' in params)
document.write(' Displaying values for <b>' + params.graph + '</b>.');
</script>
</div>
<div id="explain">
The vertical axis is measured values, and the horizontal
axis is the revision number 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>