summaryrefslogtreecommitdiffstats
path: root/web/analysis
diff options
context:
space:
mode:
Diffstat (limited to 'web/analysis')
-rw-r--r--web/analysis/TODO45
-rw-r--r--web/analysis/global.js209
-rw-r--r--web/analysis/help_tsbm.html374
-rw-r--r--web/analysis/images/ajax-spinner.gifbin0 -> 673 bytes
-rw-r--r--web/analysis/images/bg_benchmark.pngbin0 -> 186 bytes
-rw-r--r--web/analysis/images/bg_context1.pngbin0 -> 186 bytes
-rw-r--r--web/analysis/images/bg_context2.pngbin0 -> 186 bytes
-rw-r--r--web/analysis/images/bg_metric.pngbin0 -> 186 bytes
-rw-r--r--web/analysis/images/canvas-bg.pngbin0 -> 1912 bytes
-rw-r--r--web/analysis/images/help_overview.odpbin0 -> 131373 bytes
-rw-r--r--web/analysis/images/help_overview.pngbin0 -> 106001 bytes
-rw-r--r--web/analysis/images/help_overview_anno.pngbin0 -> 175285 bytes
-rw-r--r--web/analysis/images/help_plot_changes.odpbin0 -> 55566 bytes
-rw-r--r--web/analysis/images/help_plot_changes.pngbin0 -> 32275 bytes
-rw-r--r--web/analysis/images/help_plot_changes_anno.pngbin0 -> 80775 bytes
-rw-r--r--web/analysis/images/help_plot_invalidobs.odpbin0 -> 60593 bytes
-rw-r--r--web/analysis/images/help_plot_invalidobs.pngbin0 -> 42726 bytes
-rw-r--r--web/analysis/images/help_plot_invalidobs_anno.pngbin0 -> 68417 bytes
-rw-r--r--web/analysis/images/help_plot_missing.odpbin0 -> 60975 bytes
-rw-r--r--web/analysis/images/help_plot_missing.pngbin0 -> 44205 bytes
-rw-r--r--web/analysis/images/help_plot_missing_anno.pngbin0 -> 64466 bytes
-rw-r--r--web/analysis/images/help_plot_nonposobs.odpbin0 -> 55062 bytes
-rw-r--r--web/analysis/images/help_plot_nonposobs.pngbin0 -> 38328 bytes
-rw-r--r--web/analysis/images/help_plot_nonposobs_anno.pngbin0 -> 57483 bytes
-rw-r--r--web/analysis/images/help_plot_overview.odpbin0 -> 61675 bytes
-rw-r--r--web/analysis/images/help_plot_overview.pngbin0 -> 40068 bytes
-rw-r--r--web/analysis/images/help_plot_overview_anno.pngbin0 -> 80537 bytes
-rw-r--r--web/analysis/images/help_plot_rse.odpbin0 -> 74755 bytes
-rw-r--r--web/analysis/images/help_plot_rse.pngbin0 -> 50228 bytes
-rw-r--r--web/analysis/images/help_plot_rse_anno.pngbin0 -> 85506 bytes
-rw-r--r--web/analysis/images/help_plot_samplesize.odpbin0 -> 49646 bytes
-rw-r--r--web/analysis/images/help_plot_samplesize.pngbin0 -> 32995 bytes
-rw-r--r--web/analysis/images/help_plot_samplesize_anno.pngbin0 -> 52901 bytes
-rw-r--r--web/analysis/images/help_plot_selected.odpbin0 -> 110058 bytes
-rw-r--r--web/analysis/images/help_plot_selected.pngbin0 -> 79725 bytes
-rw-r--r--web/analysis/images/help_plot_selected_anno.pngbin0 -> 165685 bytes
-rw-r--r--web/analysis/images/lcms.odpbin0 -> 10987 bytes
-rw-r--r--web/analysis/images/lcms.pngbin0 -> 3710 bytes
-rw-r--r--web/analysis/images/nospinner.pngbin0 -> 193 bytes
-rw-r--r--web/analysis/images/rse.odpbin0 -> 10994 bytes
-rw-r--r--web/analysis/images/rse.pngbin0 -> 3521 bytes
-rw-r--r--web/analysis/index.html289
-rw-r--r--web/analysis/main.js571
-rw-r--r--web/analysis/rankings.js400
-rw-r--r--web/analysis/rankings.shtml147
-rw-r--r--web/analysis/rankingtabledef.html14
-rw-r--r--web/analysis/stats1.html159
-rw-r--r--web/analysis/stats1.js215
-rw-r--r--web/analysis/stats2.html485
-rw-r--r--web/analysis/stats2.js1090
-rw-r--r--web/analysis/style.css116
-rw-r--r--web/analysis/tablesorter/themes/bm/asc.pngbin0 -> 2925 bytes
-rw-r--r--web/analysis/tablesorter/themes/bm/bg.pngbin0 -> 2930 bytes
-rw-r--r--web/analysis/tablesorter/themes/bm/desc.pngbin0 -> 2893 bytes
-rw-r--r--web/analysis/tablesorter/themes/bm/style.css43
-rw-r--r--web/analysis/tsbm.js146
-rw-r--r--web/analysis/tsbm.shtml53
-rw-r--r--web/analysis/tsbmbody.html469
-rw-r--r--web/analysis/tsbmbody.js1309
-rw-r--r--web/analysis/tspbmtabledef.html26
-rw-r--r--web/analysis/tsstats.js392
-rw-r--r--web/analysis/tsstats.shtml127
62 files changed, 6679 insertions, 0 deletions
diff --git a/web/analysis/TODO b/web/analysis/TODO
new file mode 100644
index 0000000..44005d1
--- /dev/null
+++ b/web/analysis/TODO
@@ -0,0 +1,45 @@
+- Add a checkbox column (with "at most one" semantics) to the per-benchmark
+ stats table on "context 1 vs 2" page. Selecting a row will fetch result
+ details (populating Sample 1 and Sample 2 lists etc.). Selecting a row
+ will also select the same result in the plot and vice versa.
+ ... IN PROGRESS ...
+
+- Show the NRSE 1/2 values beneath the Sample 1/2 <select> lists.
+
+- Add RSE improv. factor stats to the overall stats table on
+ "context 1 vs 2" page.
+
+- Add a benchmark filter on the main page.
+
+- Implement a server-side function happenedBefore(sha11, sha12) that determines
+ if two sha1s are applied before each other in the same branch. The return
+ value is a list of 2-tuples:
+ (branch 1, sha11_first 1)
+ (branch 2, sha11_first 2)
+ ...
+ (branch n, sha11_first n)
+
+ where sha11_first i is true iff sha11 precedes (directly or indirectly)
+ sha11 in branch i.
+ NOTE: Branches that do not contain both sha11 and sha12 are not listed.
+
+ NOTE: This function can typically be implemented by a stand-alone
+ server that runs at all times. It will typically depend on some
+ book-keeping in the database (like a mapping from user-defined
+ branch names to actual git repo/branch names).
+
+- In the "available contexts" table, show average per-benchmark sample size
+ in each snapshot (e.g. next to the "last upload" time).
+
+- Add nvalid, min, and max columns to both contexts in the per-benchmark stats
+ table for two contexts comparison.
+
+- Use tablesorter for the 'available contexts' table as well
+ (using image instead of color to style cell backgrounds).
+
+- Have the client specify the histogram resolution dynamically. Currently it's
+ hardcoded to 10.
+
+- Show per-benchmark histograms using jQuery sparklines.
+
+- Write test benchmarks for internal autotesting.
diff --git a/web/analysis/global.js b/web/analysis/global.js
new file mode 100644
index 0000000..1a4148d
--- /dev/null
+++ b/web/analysis/global.js
@@ -0,0 +1,209 @@
+// --- BEGIN Global variables -----------------------------------
+
+var anySpace = / /g;
+
+// --- END Global variables -----------------------------------
+
+
+// ### 2 B DOCUMENTED!
+function updateStatus(msg, showSpinner) {
+ // Note: the "span[id^=status]" selector matches all span elements
+ // with an id attribute that begins with "status".
+ $("span[id^=status]").text("> " + msg);
+ if (showSpinner) {
+ $("img[id^=spinner]").css("display", "inline");
+ $("img[id^=nospinner]").css("display", "none");
+ } else {
+ $("img[id^=spinner]").css("display", "none");
+ $("img[id^=nospinner]").css("display", "inline");
+ }
+}
+
+// ### 2 B DOCUMENTED!
+function trim(s)
+{
+ var i;
+ var j;
+ for (i = 0; i < s.length && s[i] == ' '; ++i) ;
+ for (j = s.length - 1; j >= i && s[j] == ' '; --j) ;
+ return (i <= j) ? s.substr(i, (j - i) + 1) : "";
+}
+
+// ### 2 B DOCUMENTED!
+function queryStringArgs() {
+ pairs = document.location.search.slice(1).split("&");
+ var args = [];
+ for (i = 0; i < pairs.length; ++i) {
+ pair = pairs[i].split("=");
+ if (pair[1] == null) {
+ args[pair[0]] = "";
+ } else {
+ args[pair[0]] = trim(decodeURIComponent(pair[1]));
+ }
+ }
+ return args;
+}
+
+// ### 2 B DOCUMENTED!
+function extractArg(args, name) {
+ arg = args[name];
+ if (arg == null) {
+ return "";
+ }
+ return arg;
+}
+
+// ### 2 B DOCUMENTED!
+function isNonNullNumber(x) {
+ return (x != null) && (!isNaN(x));
+}
+
+// ### 2 B DOCUMENTED!
+// NOTE: This function is currently duplicated elsewhere in Python!
+function changeMagnitudeScore(change) {
+ var maxChange = 2.0;
+ var absChange = (change < 1.0) ? (1.0 / change) : change;
+ return (Math.min(absChange, maxChange) - 1.0) / (maxChange - 1.0);
+}
+
+// ### 2 B DOCUMENTED!
+// NOTE: This function is currently duplicated elsewhere in Python!
+function qualityScore(lsd, ni, nz, nc, mdrse) {
+ var maxBadSnapshots = 30; // experimental; maybe use max durability score?
+ var maxSampleSize = 5;
+ var maxLSD = maxBadSnapshots;
+ var maxNI = maxBadSnapshots * maxSampleSize;
+ var maxNZ = maxBadSnapshots * maxSampleSize;
+ var maxNC = maxBadSnapshots;
+
+ var lsdScore = (lsd == -1) ? 0 : Math.min(1, lsd / maxLSD);
+ var niScore = Math.min(1, ni / maxNI);
+ var nzScore = Math.min(1, nz / maxNZ);
+ var ncScore = Math.min(1, nc / maxNC);
+ var mdrseScore = (mdrse == -1) ? 0 : (mdrse / 100);
+
+ return (lsdScore + niScore + nzScore + ncScore + mdrseScore) / 5;
+}
+
+// Assigns tooltip with text 'text' to jQuery object 'obj'.
+function setTooltip(obj, text) {
+ obj.attr(
+ "title",
+ "cssheader=[tooltipHeader1] cssbody=[tooltipBody1] " +
+ "delay=[1000] " +
+ "fade=[off] " +
+ "fadespeed=[0.1] " +
+ "offsetx=[15] " +
+ "header=[] " +
+ "body=[" + text + "]"
+ );
+}
+
+function emptySHA1() {
+ var s = "";
+ var size = 40;
+ for (var i = 0; i < size; ++i)
+ s += "&nbsp;";
+ return s;
+}
+
+function dateToTimestamp(date) {
+ return Math.round(date.getTime() / 1000);
+}
+
+// Converts seconds to days.
+function secsToDays(secs) {
+ var secsInDay = 86400; // 24 * 60 * 60
+ return (secs / secsInDay).toFixed(2);
+}
+
+// Converts days to seconds.
+function daysToSecs(days) {
+ var secsInDay = 86400; // 24 * 60 * 60
+ return days * secsInDay;
+}
+
+// ### 2 B DOCUMENTED!
+function zeroPad2(s) {
+ return (s.length == 2) ? s : ("0" + s);
+}
+
+// ### 2 B DOCUMENTED!
+function ageColor(secsAgo) {
+ var secsInDay = 86400; // 24 * 60 * 60
+
+ var minSecs = 0;
+ var gradientSpan = 7; // Color varies from now to gradientSpan days ago
+ var maxSecs = gradientSpan * secsInDay;
+
+ var minR = 255;
+ var minG = 128;
+ var minB = 0;
+
+ var maxR = 228;
+ var maxG = 228;
+ var maxB = 228;
+
+ var frac = (secsAgo - minSecs) / (maxSecs - minSecs);
+ frac = Math.max(Math.min(frac, 1), 0);
+
+ var r = Math.round((1 - frac) * minR + frac * maxR);
+ var g = Math.round((1 - frac) * minG + frac * maxG);
+ var b = Math.round((1 - frac) * minB + frac * maxB);
+ var color =
+ "#" + zeroPad2(r.toString(16)) + zeroPad2(g.toString(16))
+ + zeroPad2(b.toString(16));
+ return color;
+}
+
+// ### 2 B DOCUMENTED!
+function initTablesorter() {
+ // Define a parser that handles mixed standard- and scientific notation
+ // and sorts in _descending_ order before any missing values (i.e.
+ // use this parser if large values are more interesting than small ones!).
+ $.tablesorter.addParser({
+ id: "mixed_numeric_desc_before_missing",
+ is: function(s) {
+ return false; // so this parser is not auto detected
+ },
+ format: function(s) {
+ var f = parseFloat(s);
+ if (isNaN(f))
+ return "";
+
+ // The following hack ensures that zeros are grouped together
+ // when sorting. True zero values should be avoided as they appear
+ // to get the same sorting rank as undefined/missing values.
+ if (f == 0)
+ return Number.MIN_VALUE;
+
+ return f.toFixed(20); // max precision guaranteed by ECMA standard
+ },
+ type: "numeric"
+ });
+
+ // Define a parser that handles mixed standard- and scientific notation and
+ // sorts in _ascending_ order before any missing values (i.e.
+ // use this parser if small values are more interesting than large ones!).
+ $.tablesorter.addParser({
+ id: "mixed_numeric_asc_before_missing",
+ is: function(s) {
+ return false; // so this parser is not auto detected
+ },
+ format: function(s) {
+ var f = parseFloat(s);
+ if (isNaN(f))
+ return "";
+
+ // The following inversion ensures that values are sorted in
+ // ascending order before any missing values.
+ // WARNING: This technique assumes non-negative input!
+ // Note also that zero-division is not an issue, since "infinity"
+ // is handled in a safe way.
+ f = 1.0 / f;
+
+ return f.toFixed(20); // max precision guaranteed by ECMA standard
+ },
+ type: "numeric"
+ });
+}
diff --git a/web/analysis/help_tsbm.html b/web/analysis/help_tsbm.html
new file mode 100644
index 0000000..e467fe4
--- /dev/null
+++ b/web/analysis/help_tsbm.html
@@ -0,0 +1,374 @@
+<html>
+
+<head>
+
+<style type="text/css">
+table { border-collapse:collapse; }
+table, th, td { border: 1px solid #aaaaaa; }
+th {vertical-align:top; padding:10px}
+td {vertical-align:top; padding:10px}
+li {padding:5px}
+</style>
+
+</head>
+
+<body>
+
+<span style="text-align:center">
+<h1>BM2 Documentation</h1>
+<h2><em>Benchmark Time Series Page</em></h2>
+</span>
+
+
+<!-- ///////////////////////////////////////////////////////////////// -->
+<h2>Overview</h2>
+&nbsp;<img src="images/help_overview_anno.png" /><br /><br />
+
+<h2>Main context</h2>
+
+<b>Note:</b> Some of the terms and concepts in the below table are described
+in greater detail elsewhere. The additional documentation can often be
+accessed through a link.
+<br /><br />
+
+<table>
+ <tr>
+ <td style="text-align:right"><b>Database</b></td>
+ <td>
+ The database from which the results were extracted.
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right"><b>Report date</b></td>
+ <td>
+ The date at which the web page was generated.
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right"><b>Host</b></td>
+ <td>
+ The physical computer on which the benchmark producing the results
+ was executed. <b>Note:</b>It is assumed that the HW/SW specifications of
+ the host does not change significantly during the time span of the time
+ series. (The principle here being that significant changes in the time
+ series should be caused by changes in the product (i.e. Qt) only!)
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right"><b>Platform</b></td>
+ <td>
+ The general environment used for building and executing the product
+ being measured (typically an OS/compiler combination).
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right"><b>Branch</b></td>
+ <td>
+ Essentially the version of the product being measured. The branch is
+ normally made up of two components: The <i>git repository</i> and the
+ <i>git branch</i>.
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right"><b>Target snapshots</b></td>
+ <td>
+ The requested subsequence of snapshots for which relevant results
+ potentially exist in the database. The number of target snapshots is
+ shown in parentheses.
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right"><b>Difference tolerance</b></td>
+ <td>
+ A real value &ge; 1 that decides whether a <a href="#changes">change</a>
+ between two median observations is considered significant or not.
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right">
+ <a name="mindurtol" />
+ <b>Minimum durability tolerance</b>
+ </td>
+ <td>
+ The minimum length a contiguous sequence of significantly equal median
+ observations must have for it to achieve a durability score greater
+ than zero. Once the sequence is at least this long, the durability
+ score grows linearly to 1 at a rate that depends on the maximum
+ durability tolerance.
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right">
+ <a name="maxdurtol" />
+ <b>Maximum durability tolerance</b>
+ </td>
+ <td>
+ The length of a contiguous sequence of significantly equal median
+ observations that is sufficient to achieve the maximum durability score
+ of 1. The durability score for shorter sequences falls linearly to 0 at
+ a rate that depends on the minimum durability tolerance.
+ </td>
+ </tr>
+</table>
+
+
+<h2>Time series statistics</h2>
+
+<b>Note:</b> Some of the terms and concepts in the below table are described
+in greater detail elsewhere. The additional documentation can often be
+accessed through a link.
+<br /><br />
+
+<table>
+ <tr>
+ <td style="text-align:right"><b>MS</b></td>
+ <td>
+ Missing snapshots, i.e. the number of target snapshots for which no
+ results exist.
+ <br /><br />A high value might indicate unstable execution.
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right"><b>LSD</b></td>
+ <td>
+ Last Snapshot Distance, i.e. distance between the last target snapshot
+ and the last snapshot in the time series.
+ <br /><br />If the last target snapshot is the last one available in the
+ database, a high value might indicate that the benchmark currently fails
+ to produce results.
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right"><b>NI</b></td>
+ <td>
+ Total number of observations explicitly flagged as invalid.
+ <br /><br />An invalid observation is typically caused by a failed
+ QVERIFY() etc.
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right"><b>NZ</b></td>
+ <td>
+ Total number of non-positive observations.
+ <br /><br />Normally an observation must be positive to be valid.
+ <br /><b>Note:</b> A non-positive observation is not necessarily flagged
+ as <i>invalid</i> (see <b>NI</b>).
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right"><b>NC</b></td>
+ <td>
+ Number of <a href="#changes">significant changes</a>.
+ <br /><br />A high value might indicate unstable or fluctuating results.
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right"><b>MDRSE</b></td>
+ <td>
+ Median of the
+ valid <a href="http://en.wikipedia.org/wiki/Standard_error_%28statistics%29#Relative_standard_error">relative
+ standard errors</a> of all snapshots.
+ <br /><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;
+ <img src="images/rse.png" />
+ <br /><br />A high value might indicate unstable or fluctuating results.
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right"><b>RSEMD</b></td>
+ <td>
+ Relative standard error (see above) of the valid median observations of
+ all snapshots.
+ <br /><br />A high value might indicate either 1) unstable or
+ fluctuating results or 2) stable changes of a high magnitude.
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right"><b>LC</b></td>
+ <td>
+ Last <a href="#changes">significant change</a>.
+ <br /><br />The higher the value is above 1, the more strongly it
+ represents an improvement.
+ <br />The lower the value is below 1, the more strongly it represents a
+ regression.
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right"><b>LCDA</b></td>
+ <td>
+ Days ago (relative to the report date) since the first observation for
+ the last <a href="#changes">significant change</a> snapshot was uploaded
+ to the database.
+ <br />The distance (in terms of number of target snapshots) between the
+ last significant change snapshot and the last target snapshot is shown
+ in parentheses.
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right"><b>LCMS</b></td>
+ <td>
+ Magnitude score of the last <a href="#changes">significant
+ change</a>. This score indicates the strength of the last signicifant
+ change as a value ranging from 0 (weak) to 1 (strong):
+ <br /><br />&nbsp;&nbsp;&nbsp;&nbsp;
+ <img src="images/lcms.png" />
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right"><b>LCGSS</b></td>
+ <td>
+ Global separation score for the last <a href="#changes">significant
+ change</a>. This score indicates how well the median observation at the
+ last significant change snapshot are separated from the median
+ observations at <u>all preceding</u> snapshots in the time series. The
+ median observation at the last <a href="#changes">base snapshot</a> is
+ used as the maximum separation reference.
+
+ <br /><br />The score ranges from 0 (weak separation) to 1 (strong
+ separation).
+
+ <br /><br />This score roughly measures how close the median observation
+ at the last significant change is to represent an "all time high(low)"
+ up to this point in the history.
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right"><b>LCLSS</b></td>
+ <td>
+ Local separation score for the last <a href="#changes">significant
+ change</a>. This score indicates how well the median observations on
+ each side of the last significant change snapshot are separated from
+ each other. Snapshots before the last <a href="#changes">base
+ snapshot</a> are not considered. The median observation at the base
+ snapshot is used as the maximum separation reference.
+
+ <br /><br />The score ranges from 0 (weak separation) to 1 (strong
+ separation).
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right"><b>LCDS1</b></td>
+ <td>
+ Durability score 1 for the last <a href="#changes">significant
+ change</a>. This score indicates the distance (in terms of number of
+ snapshots) from the last significant change to
+ its <a href="#changes">base snapshot</a>.
+
+ <br /><br />The score ranges from 0 (weak durability) to 1 (strong
+ durability) and is scaled against
+ the <a href="#mindurtol">min</a>/<a href="#maxdurtol">max</a> durability
+ tolerances.
+
+ <br /><br />This score measures for how long the median observation
+ stayed near the base value until the last significant change occurred.
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right"><b>LCDS2</b></td>
+ <td>
+ Durability score 2 for the last <a href="#changes">significant
+ change</a>. This score indicates the distance (in terms of number of
+ snapshots) from the last significant change to the end of the time
+ series.
+
+ <br /><br />The score ranges from 0 (weak durability) to 1 (strong
+ durability) and is scaled against
+ the <a href="#mindurtol">min</a>/<a href="#maxdurtol">max</a> durability
+ tolerances.
+
+ <br /><br />This score measures for how long the median observation at
+ the last significant change has stayed essentially the same.
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right"><b>LCSS</b></td>
+ <td>
+ Stability score for the last <a href="#changes">significant change</a>:
+ <br /><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;
+ <b>LCMS</b> * <b>LCGSS</b> * <b>LCLSS</b> * <b>LCDS1</b> * <b>LCDS2</b>
+ <br /><br />
+ The higher this score, the higher the likelihood that the last
+ significant change is or will become permanent.
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:right"><b>LCSS1</b></td>
+ <td>
+ Stability score for the last <a href="#changes">significant change</a>
+ that does not consider the history after the latter:
+ <br /><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;
+ <b>LCMS</b> * <b>LCGSS</b> * <b>LCLSS</b> * <b>LCDS1</b>
+ <br /><br />
+ The higher this score, the higher the likelihood that the last
+ signicifant change is or will become permanent, but since <b>LCDS2</b>
+ is omitted from the product, a high <b>LCSS1</b> is more likely to be
+ caused by an outlier than a high <b>LCSS</b>!
+ </td>
+ </tr>
+</table>
+
+
+
+<h2>Benchmark and metric</h2>
+The benchmark name consists of three subnames and is formatted like this:
+<br /><br />
+&lt;subname 1&gt;<b>:</b>&lt;subname 2&gt;<b>(</b>&lt;subname 3&gt;<b>)</b>
+<br /><br />
+
+The subnames can essentially be anything not containing the characters
+'<b>:</b>', '<b>(</b>', and '<b>)</b>'. Only subname 3 may contain whitespace.
+
+For benchmark results generated by QTestLib, the subnames always correspond to
+<i>test case</i>, <i>test function</i>, and <i>data tag</i> respectively.
+
+<br /><br />
+The metric name is one of a set of predefined metric names, each of which is
+classified as either "lower is better" (like walltime) or "higher is better"
+(like fps).
+
+<!-- ///////////////////////////////////////////////////////////////// -->
+<h2>Time Series Plot</h2>
+
+<h3>Snapshots and main graph</h3>
+&nbsp;<img src="images/help_plot_overview_anno.png" /><br /><br />
+
+
+<a name="changes" />
+<h3>Significant changes</h3>
+&nbsp;<img src="images/help_plot_changes_anno.png" /><br /><br />
+
+
+<h3>Sample size</h3>
+&nbsp;<img src="images/help_plot_samplesize_anno.png" /><br /><br />
+
+
+<h3>Non-positive observations</h3>
+&nbsp;<img src="images/help_plot_nonposobs_anno.png" /><br /><br />
+
+
+<h3>Invalid observations</h3>
+&nbsp;<img src="images/help_plot_invalidobs_anno.png" /><br /><br />
+
+
+<h3>Statistical dispersion</h3>
+Statistical dispersion in a time series is measured in terms of
+<a href="http://en.wikipedia.org/wiki/Standard_error_%28statistics%29#Relative_standard_error">relative standard error</a> (RSE).
+&nbsp;<img src="images/help_plot_rse_anno.png" /><br /><br />
+
+
+<h3>Missing data</h3>
+&nbsp;<img src="images/help_plot_missing_anno.png" /><br /><br />
+
+
+<h2>Snapshot details</h2>
+When clicking on a snapshot in the plot, the two tables below the plot are
+filled with various details about the selected snapshot.
+<br />
+<br />
+&nbsp;<img src="images/help_plot_selected_anno.png" /><br /><br />
+
+</body>
+
+</html>
diff --git a/web/analysis/images/ajax-spinner.gif b/web/analysis/images/ajax-spinner.gif
new file mode 100644
index 0000000..d0bce15
--- /dev/null
+++ b/web/analysis/images/ajax-spinner.gif
Binary files differ
diff --git a/web/analysis/images/bg_benchmark.png b/web/analysis/images/bg_benchmark.png
new file mode 100644
index 0000000..5fc3d66
--- /dev/null
+++ b/web/analysis/images/bg_benchmark.png
Binary files differ
diff --git a/web/analysis/images/bg_context1.png b/web/analysis/images/bg_context1.png
new file mode 100644
index 0000000..b8bafa3
--- /dev/null
+++ b/web/analysis/images/bg_context1.png
Binary files differ
diff --git a/web/analysis/images/bg_context2.png b/web/analysis/images/bg_context2.png
new file mode 100644
index 0000000..4dc7c87
--- /dev/null
+++ b/web/analysis/images/bg_context2.png
Binary files differ
diff --git a/web/analysis/images/bg_metric.png b/web/analysis/images/bg_metric.png
new file mode 100644
index 0000000..0d4c135
--- /dev/null
+++ b/web/analysis/images/bg_metric.png
Binary files differ
diff --git a/web/analysis/images/canvas-bg.png b/web/analysis/images/canvas-bg.png
new file mode 100644
index 0000000..93701d4
--- /dev/null
+++ b/web/analysis/images/canvas-bg.png
Binary files differ
diff --git a/web/analysis/images/help_overview.odp b/web/analysis/images/help_overview.odp
new file mode 100644
index 0000000..7d85b7b
--- /dev/null
+++ b/web/analysis/images/help_overview.odp
Binary files differ
diff --git a/web/analysis/images/help_overview.png b/web/analysis/images/help_overview.png
new file mode 100644
index 0000000..a184b06
--- /dev/null
+++ b/web/analysis/images/help_overview.png
Binary files differ
diff --git a/web/analysis/images/help_overview_anno.png b/web/analysis/images/help_overview_anno.png
new file mode 100644
index 0000000..8df94a1
--- /dev/null
+++ b/web/analysis/images/help_overview_anno.png
Binary files differ
diff --git a/web/analysis/images/help_plot_changes.odp b/web/analysis/images/help_plot_changes.odp
new file mode 100644
index 0000000..dc60331
--- /dev/null
+++ b/web/analysis/images/help_plot_changes.odp
Binary files differ
diff --git a/web/analysis/images/help_plot_changes.png b/web/analysis/images/help_plot_changes.png
new file mode 100644
index 0000000..08817e9
--- /dev/null
+++ b/web/analysis/images/help_plot_changes.png
Binary files differ
diff --git a/web/analysis/images/help_plot_changes_anno.png b/web/analysis/images/help_plot_changes_anno.png
new file mode 100644
index 0000000..791db6a
--- /dev/null
+++ b/web/analysis/images/help_plot_changes_anno.png
Binary files differ
diff --git a/web/analysis/images/help_plot_invalidobs.odp b/web/analysis/images/help_plot_invalidobs.odp
new file mode 100644
index 0000000..1c3e957
--- /dev/null
+++ b/web/analysis/images/help_plot_invalidobs.odp
Binary files differ
diff --git a/web/analysis/images/help_plot_invalidobs.png b/web/analysis/images/help_plot_invalidobs.png
new file mode 100644
index 0000000..74f80de
--- /dev/null
+++ b/web/analysis/images/help_plot_invalidobs.png
Binary files differ
diff --git a/web/analysis/images/help_plot_invalidobs_anno.png b/web/analysis/images/help_plot_invalidobs_anno.png
new file mode 100644
index 0000000..31ddecb
--- /dev/null
+++ b/web/analysis/images/help_plot_invalidobs_anno.png
Binary files differ
diff --git a/web/analysis/images/help_plot_missing.odp b/web/analysis/images/help_plot_missing.odp
new file mode 100644
index 0000000..61d6a98
--- /dev/null
+++ b/web/analysis/images/help_plot_missing.odp
Binary files differ
diff --git a/web/analysis/images/help_plot_missing.png b/web/analysis/images/help_plot_missing.png
new file mode 100644
index 0000000..7165797
--- /dev/null
+++ b/web/analysis/images/help_plot_missing.png
Binary files differ
diff --git a/web/analysis/images/help_plot_missing_anno.png b/web/analysis/images/help_plot_missing_anno.png
new file mode 100644
index 0000000..00359d7
--- /dev/null
+++ b/web/analysis/images/help_plot_missing_anno.png
Binary files differ
diff --git a/web/analysis/images/help_plot_nonposobs.odp b/web/analysis/images/help_plot_nonposobs.odp
new file mode 100644
index 0000000..fa50e67
--- /dev/null
+++ b/web/analysis/images/help_plot_nonposobs.odp
Binary files differ
diff --git a/web/analysis/images/help_plot_nonposobs.png b/web/analysis/images/help_plot_nonposobs.png
new file mode 100644
index 0000000..4805862
--- /dev/null
+++ b/web/analysis/images/help_plot_nonposobs.png
Binary files differ
diff --git a/web/analysis/images/help_plot_nonposobs_anno.png b/web/analysis/images/help_plot_nonposobs_anno.png
new file mode 100644
index 0000000..980dd46
--- /dev/null
+++ b/web/analysis/images/help_plot_nonposobs_anno.png
Binary files differ
diff --git a/web/analysis/images/help_plot_overview.odp b/web/analysis/images/help_plot_overview.odp
new file mode 100644
index 0000000..8204ee5
--- /dev/null
+++ b/web/analysis/images/help_plot_overview.odp
Binary files differ
diff --git a/web/analysis/images/help_plot_overview.png b/web/analysis/images/help_plot_overview.png
new file mode 100644
index 0000000..c2a1832
--- /dev/null
+++ b/web/analysis/images/help_plot_overview.png
Binary files differ
diff --git a/web/analysis/images/help_plot_overview_anno.png b/web/analysis/images/help_plot_overview_anno.png
new file mode 100644
index 0000000..3affed7
--- /dev/null
+++ b/web/analysis/images/help_plot_overview_anno.png
Binary files differ
diff --git a/web/analysis/images/help_plot_rse.odp b/web/analysis/images/help_plot_rse.odp
new file mode 100644
index 0000000..03e44f4
--- /dev/null
+++ b/web/analysis/images/help_plot_rse.odp
Binary files differ
diff --git a/web/analysis/images/help_plot_rse.png b/web/analysis/images/help_plot_rse.png
new file mode 100644
index 0000000..b04fa9b
--- /dev/null
+++ b/web/analysis/images/help_plot_rse.png
Binary files differ
diff --git a/web/analysis/images/help_plot_rse_anno.png b/web/analysis/images/help_plot_rse_anno.png
new file mode 100644
index 0000000..555e6f3
--- /dev/null
+++ b/web/analysis/images/help_plot_rse_anno.png
Binary files differ
diff --git a/web/analysis/images/help_plot_samplesize.odp b/web/analysis/images/help_plot_samplesize.odp
new file mode 100644
index 0000000..cd73075
--- /dev/null
+++ b/web/analysis/images/help_plot_samplesize.odp
Binary files differ
diff --git a/web/analysis/images/help_plot_samplesize.png b/web/analysis/images/help_plot_samplesize.png
new file mode 100644
index 0000000..325f571
--- /dev/null
+++ b/web/analysis/images/help_plot_samplesize.png
Binary files differ
diff --git a/web/analysis/images/help_plot_samplesize_anno.png b/web/analysis/images/help_plot_samplesize_anno.png
new file mode 100644
index 0000000..6082b59
--- /dev/null
+++ b/web/analysis/images/help_plot_samplesize_anno.png
Binary files differ
diff --git a/web/analysis/images/help_plot_selected.odp b/web/analysis/images/help_plot_selected.odp
new file mode 100644
index 0000000..77a3f79
--- /dev/null
+++ b/web/analysis/images/help_plot_selected.odp
Binary files differ
diff --git a/web/analysis/images/help_plot_selected.png b/web/analysis/images/help_plot_selected.png
new file mode 100644
index 0000000..4f52576
--- /dev/null
+++ b/web/analysis/images/help_plot_selected.png
Binary files differ
diff --git a/web/analysis/images/help_plot_selected_anno.png b/web/analysis/images/help_plot_selected_anno.png
new file mode 100644
index 0000000..d3a1a08
--- /dev/null
+++ b/web/analysis/images/help_plot_selected_anno.png
Binary files differ
diff --git a/web/analysis/images/lcms.odp b/web/analysis/images/lcms.odp
new file mode 100644
index 0000000..6b79925
--- /dev/null
+++ b/web/analysis/images/lcms.odp
Binary files differ
diff --git a/web/analysis/images/lcms.png b/web/analysis/images/lcms.png
new file mode 100644
index 0000000..0bcd6ad
--- /dev/null
+++ b/web/analysis/images/lcms.png
Binary files differ
diff --git a/web/analysis/images/nospinner.png b/web/analysis/images/nospinner.png
new file mode 100644
index 0000000..4f95459
--- /dev/null
+++ b/web/analysis/images/nospinner.png
Binary files differ
diff --git a/web/analysis/images/rse.odp b/web/analysis/images/rse.odp
new file mode 100644
index 0000000..9effed8
--- /dev/null
+++ b/web/analysis/images/rse.odp
Binary files differ
diff --git a/web/analysis/images/rse.png b/web/analysis/images/rse.png
new file mode 100644
index 0000000..b75661b
--- /dev/null
+++ b/web/analysis/images/rse.png
Binary files differ
diff --git a/web/analysis/index.html b/web/analysis/index.html
new file mode 100644
index 0000000..53da056
--- /dev/null
+++ b/web/analysis/index.html
@@ -0,0 +1,289 @@
+<html>
+
+<head>
+ <title>BM Analysis</title>
+ <script type="text/javascript" src="jquery-1.4.2.min.js"></script>
+ <script type="text/javascript" src="boxover/boxover.js"></script>
+ <script type="text/javascript" src="global.js"></script>
+ <script type="text/javascript" src="main.js"></script>
+ <link rel="stylesheet" href="style.css" type="text/css" />
+
+</head>
+
+<body>
+
+<!--
+<img id="underConstruction" src="images/under_construction.jpg" />
+<br/>
+<script type="text/javascript">
+ setTooltip(
+ $("#underConstruction"),
+ "This page is currently under construction. It may show " +
+ "nonsense/limited information or even be completely broken.");
+</script>
+-->
+
+<span id="title" style="font-size:18; font-weight:bold">BM Analysis</span>
+&nbsp;&nbsp;&nbsp;
+
+<span style="white-space:nowrap">
+<span id="status">no status</span>
+<img alt="spinner" id="spinner" src="images/ajax-spinner.gif"
+ style="display:none"/>
+</span>
+
+<br />
+
+<a id="mainPageLink" href="">main page</a>
+<script type="text/javascript">
+$("#mainPageLink").attr("href", "http://" + location.host + "/bm2");
+</script>
+
+<div id="div_analysisPageLink" style="display:inline">
+&nbsp;&nbsp;
+<a id="analysisPageLink" href="">analysis page (all types)</a>
+<script type="text/javascript">
+$("#analysisPageLink").attr(
+ "href", "http://" + location.host + "/bm2/analysis");
+</script>
+</div>
+
+<div id="div_analysisPageRankedOnlyLink" style="display:inline">
+&nbsp;&nbsp;
+<a id="analysisPageRankedOnlyLink" href="">
+ analysis page (rankings only)</a>
+<script type="text/javascript">
+$("#analysisPageRankedOnlyLink").attr(
+ "href", "http://" + location.host + "/bm2/analysis?rankedonly=1");
+</script>
+</div>
+
+<div id="div_availableContexts" style="display:none">
+
+<br />
+<table id="actionTable">
+ <tr id="actionTable_header"><th>Action</th><th>Arguments</th></tr>
+ <tr>
+ <td style="padding-top:5px; padding-bottom:5px;">
+ <a class="disabledActionButton" id="action_stats1"
+ href="javascript::void(0)" onclick="return false">
+ Show basic statistics for Context <span class="context1">
+ &nbsp;1&nbsp;</span></a>
+ <script type="text/javascript">
+ setTooltip(
+ $("#action_stats1"),
+ "For Context 1, this action shows basic statistics for " +
+ "the observations produced by each benchmark.");
+ </script>
+ </td><td></td>
+ </tr>
+ <tr id="actionTable_rankings">
+ <td style="padding-top:5px; padding-bottom:5px;">
+ <a class="disabledActionButton" id="action_rankings"
+ href="javascript::void(0)" onclick="return false">
+ Show rankings for Context <span class="context1">
+ &nbsp;1&nbsp;</span></a>
+ <script type="text/javascript">
+ setTooltip(
+ $("#action_rankings"),
+ "For Context 1, this action ranks benchmarks on different " +
+ "criteria.<br /><br />The idea is to prioritize benchmarks " +
+ "that deserve attention (either because they reflect stable " +
+ "changes in Qt, or because they need to improve their own " +
+ "quality)." +
+ "<br /><br /><b>Note:</b> This action is enabled iff " +
+ "an 'R' is indicated for the snapshot of Context 1, " +
+ "thus indicating availability of rankings in the " +
+ "database.");
+ </script>
+ </td>
+ <td id="max_rank_size_lead">Maximum size:
+ <select id="max_rank_size" onchange="updateActions()">
+ <option value="5">5</option>
+ <option value="10">10</option>
+ <option value="20">20</option>
+ <option value="50">50</option>
+ <option value="100">100</option>
+ <option value="200">200</option>
+ <option value="500">500</option>
+ <option value="1000">1000</option>
+ <option value="-1">unlimited</option>
+ </select>
+ <script type="text/javascript">
+ setTooltip(
+ $("#max_rank_size_lead"),
+ "The maximum number of benchmarks to show in each ranking.");
+ </script>
+ </td>
+ </tr>
+ <tr>
+ <td style="padding-top:5px; padding-bottom:5px;">
+ <a class="disabledActionButton" id="action_stats2"
+ href="javascript:void(0)" onclick="return false">
+ Compare Context <span class="context2">&nbsp;2&nbsp;</span> to Context
+ <span class="context1">&nbsp;1&nbsp;</span></a>
+ <script type="text/javascript">
+ setTooltip(
+ $("#action_stats2"),
+ "This action shows how Context 2 compares directly to Context 1 " +
+ "in terms of the per-benchmark median observations.");
+ </script>
+ </td><td></td>
+ </tr>
+ <tr>
+ <td style="padding-top:5px; padding-bottom:5px;">
+ <a class="disabledActionButton" id="action_tsstats"
+ href="javascript::void(0)" onclick="return false">
+ Show time series from Context <span class="context1">
+ &nbsp;1&nbsp;</span> to Context <span class="context2">
+ &nbsp;2&nbsp;</span></a>
+ <script type="text/javascript">
+ setTooltip(
+ $("#action_tsstats"),
+ "For the snapshot interval defined by Context 1 and 2, this " +
+ "action shows statistics (for significant changes in the " +
+ "median observation etc.) for all " +
+ "per-benchmark time series. Moreover, the time series of a " +
+ "selected benchmark is shown in a graphical plot." +
+ "<br /><br /><b>Note:</b> This action is enabled by " +
+ "specifying Context 1 and 2 so that the host/platform/branch " +
+ "combinations are <u>identical</u>, while the " +
+ "snapshots are <u>different</u>.");
+ </script>
+ (<span id="tsstats_nsnapshots">0</span>&nbsp;snapshots)
+ </td>
+ <td id="diff_tol_lead">Difference tolerance:
+ <select id="diff_tol" onchange="updateActions()">
+ <option value="1.0">1.0</option>
+ <option value="1.01">1.01</option>
+ <option value="1.02">1.02</option>
+ <option value="1.05">1.05</option>
+ <option value="1.1">1.1</option>
+ <option value="1.2">1.2</option>
+ <option value="1.5">1.5</option>
+ <option value="2">2</option>
+ <option value="5">5</option>
+ </select>
+ <script type="text/javascript">
+ setTooltip(
+ $("#diff_tol_lead"),
+ "A <i>change</i> is the ratio of " +
+ "the median observation in two snapshots. " +
+ "A change is considered <i>significant</i> " +
+ "if it is either greater than <i>DFT</i> " +
+ "or less than 1&nbsp;/&nbsp;<i>DFT</i>, where <i>DFT</i> " +
+ "is the difference tolerance.");
+ </script>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td id="dur_tol_min_lead">
+ Minimum durability tolerance:
+ <select id="dur_tol_min" onchange="updateActions()">
+ <option value="1">1</option>
+ <option value="2">2</option>
+ <option value="3">3</option>
+ <option value="4">4</option>
+ <option value="5">5</option>
+ <option value="5">6</option>
+ </select>
+ <script type="text/javascript">
+ setTooltip(
+ $("#dur_tol_min_lead"),
+ "The minimum length a contiguous sequence of significantly " +
+ "equal median observations must have for it to achieve a " +
+ "durability score greater than zero. Once the sequence is " +
+ "at least this long, the durability score grows linearly " +
+ "to 1 at a rate that depends on the maximum durability " +
+ "tolerance.");
+ </script>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td id="dur_tol_max_lead">
+ Maximum durability tolerance:
+ <select id="dur_tol_max" onchange="updateActions()">
+ <option value="10">10</option>
+ <option value="20">20</option>
+ <option value="30">30</option>
+ <option value="40">40</option>
+ <option value="50">50</option>
+ <option value="60">60</option>
+ <option value="70">70</option>
+ <option value="80">80</option>
+ <option value="90">90</option>
+ <option value="100">100</option>
+ </select>
+ <script type="text/javascript">
+ setTooltip(
+ $("#dur_tol_max_lead"),
+ "The length of a contiguous sequence of significantly equal " +
+ "median observations that is sufficient to achieve the " +
+ "maximum durability score of 1. The durability score for " +
+ "shorter sequences falls linearly to 0 at a rate that " +
+ "depends on the minimum durability tolerance.");
+ </script>
+ </td>
+ </tr>
+</table>
+<br />
+<br />
+
+<span style="font-size:14; font-weight:bold">Contexts:</span>
+
+<br />
+<div style="margin-top:5px; margin-bottom:5px;">
+<b>Database:</b>
+<select id="database" onchange="selectDatabase()">
+ <option value="bm">bm</option>
+ <option value="bm-dev">bm-dev (sandbox)</option>
+</select>
+</div>
+
+<table id="contextsTable">
+<thead>
+<tr>
+ <th id="context1" class="context1">1</th>
+ <script type="text/javascript">setTooltip($("#context1"), "Context 1")
+ </script>
+ <th id="context2" class="context2">2</th>
+ <script type="text/javascript">setTooltip($("#context2"), "Context 2")
+ </script>
+ <th>Host</th>
+ <th>Platform</th>
+ <th>Branch</th>
+ <th>Snapshot</th>
+</tr>
+</thead>
+<tbody>
+</tbody>
+</table>
+
+<div id="div_noMatchingContexts" style="display:none">
+<span style="font-size:14; color:red">--- no matching contexts ---</span>
+</div>
+
+<div id="div_matchingTestCases" style="display:none">
+<br />
+<br />
+<span style="font-size:14; font-weight:bold">Test Cases:</span>
+<a href="javascript::void(0)" onclick="selectAllTestCases(); return false;">
+ select all</a>
+&nbsp;&nbsp;&nbsp;
+<a href="javascript::void(0)" onclick="clearAllTestCases(); return false;">
+ clear all</a>
+<table id="testCasesTable">
+<thead>
+</thead>
+<tbody>
+</tbody>
+</table>
+
+</div>
+
+</div>
+
+</body>
+</html>
diff --git a/web/analysis/main.js b/web/analysis/main.js
new file mode 100644
index 0000000..30b5401
--- /dev/null
+++ b/web/analysis/main.js
@@ -0,0 +1,571 @@
+// --- BEGIN Global variables -----------------------------------
+
+var rankedOnly = null; // Whether to adapt GUI to only show
+ // information related to rankings
+
+var testCaseChecked = new Array();
+
+// --- END Global variables -------------------------------------
+
+
+
+function selectAllTestCases() {
+ $("#testCasesTable input").attr("checked", true);
+ updateActions();
+}
+
+function clearAllTestCases() {
+ $("#testCasesTable input").attr("checked", false);
+ updateActions();
+}
+
+function fetchTestCases() {
+ updateStatus("fetching matching test cases ...", true);
+
+ $("#div_matchingTestCases").css("display", "none");
+
+ context1_ = context1();
+ if (context1_ == null) {
+ updateStatus("fetching matching test cases ... done", false);
+ updateStatus("", false);
+ return;
+ }
+ context2_ = context2();
+
+ // Save current selection ...
+ $("#testCasesTable input").each(function() {
+ testCaseChecked[this.name] = this.checked;
+ });
+
+ query = "?db=" + $('#database').val();
+
+ if (context2_["host"] == "") {
+ query += "&cmd=testcases1";
+ query += "&host=" + encodeURIComponent(context1_["host"]);
+ query += "&platform=" + encodeURIComponent(context1_["platform"]);
+ query += "&branch=" + encodeURIComponent(context1_["branch"]);
+ query += "&sha1=" + context1_["sha1"];
+ } else {
+ query += "&cmd=testcases2";
+ query += "&host1=" + encodeURIComponent(context1_["host"]);
+ query += "&platform1=" + encodeURIComponent(context1_["platform"]);
+ query += "&branch1=" + encodeURIComponent(context1_["branch"]);
+ query += "&sha11=" + context1_["sha1"];
+ query += "&host2=" + encodeURIComponent(context2_["host"]);
+ query += "&platform2=" + encodeURIComponent(context2_["platform"]);
+ query += "&branch2=" + encodeURIComponent(context2_["branch"]);
+ query += "&sha12=" + context2_["sha1"];
+ }
+
+ url = "http://" + location.host + "/cgi-bin/getstatswrapper" + query;
+ // alert("url: >" + url + "<");
+
+ $.ajax({
+ url: url,
+ type: "GET",
+ dataType: "json",
+
+ success: function(data, textStatus, request) {
+ if (request.readyState == 4) {
+ if (request.status == 200) {
+
+ if (data.error != null) {
+ updateStatus(
+ "fetching matching test cases ... failed: " +
+ data.error, false);
+ return
+ }
+
+ updateStatus(
+ "fetching matching test cases ... done", false);
+ updateStatus("", false);
+
+ // Remove all rows ...
+ $("#testCasesTable tr").remove();
+
+ // Insert new rows ...
+ testCases = data.testcases;
+ html = "";
+ for (i = 0; i < testCases.length; ++i) {
+ testCase = testCases[i];
+ html += "<tr>";
+ html += "<td><input name=\"" + testCase + "\"" +
+ "type=\"checkbox\" " +
+ "onclick=\"updateActions()\"></td>";
+ html += "<td>" + testCase + "</td>";
+ html += "</tr>";
+ }
+ $("#testCasesTable > tbody:last").append(html);
+
+ // Select all rows ...
+ //$("#testCasesTable input").attr("checked", "true");
+
+ // Restore last selection as much as possible ...
+ $("#testCasesTable input").each(function() {
+ if (this.name in testCaseChecked) {
+ this.checked = testCaseChecked[this.name];
+ } else {
+ this.checked = true;
+ }
+ });
+
+ $("#div_matchingTestCases").css("display", "block");
+
+ updateActions();
+ }
+ }
+ },
+
+ error: function(request, textStatus, errorThrown) {
+ descr = errorThrown;
+ if (errorThrown == null) {
+ descr = "undefined error - is the server down?";
+ }
+ updateStatus(
+ "fetching matching test cases ... error: " + descr, false);
+ }
+
+ // complete: function(request, textStatus) {
+ // alert("complete; request.status: " + request.status)
+ // }
+
+ });
+
+ return false;
+}
+
+function setActionUrl(id, url) {
+ sel = $("#" + id);
+ if (url != "") {
+ sel.removeAttr("onclick");
+ sel.attr("class", "actionButton");
+ sel.attr("href", url);
+ } else {
+ sel.attr("onclick", "return false");
+ sel.attr("class", "disabledActionButton");
+ sel.attr("href", "javascript::void(0)");
+ }
+}
+
+function updateActions() {
+ context1_ = context1();
+ if (context1_ == null) { // special case: no contexts are available
+ setActionUrl("action_stats1", "");
+ setActionUrl("action_rankings", "");
+ setActionUrl("action_stats2", "");
+ setActionUrl("action_tsstats", "");
+ return;
+ }
+ context2_ = context2();
+
+ testCaseFilter = "";
+ $("#testCasesTable input").each(function() {
+ if (this.checked) {
+ testCaseFilter += " " + this.name;
+ }
+ });
+
+ // --- stats1 ---
+ url_stats1 = "stats1.html";
+ url_stats1 += "?db=" + $('#database').val();
+ url_stats1 += "&cmd=stats1";
+ url_stats1 += "&host=" + encodeURIComponent(context1_["host"]);
+ url_stats1 += "&platform=" + encodeURIComponent(context1_["platform"]);
+ url_stats1 += "&branch=" + encodeURIComponent(context1_["branch"]);
+ url_stats1 += "&sha1=" + context1_["sha1"];
+ url_stats1 += "&testcasefilter=" + testCaseFilter;
+
+ // --- rankings ---
+ if (context1_["rankingsExist"]) {
+ url_rankings = "rankings.shtml";
+ url_rankings += "?db=" + $('#database').val();
+ url_rankings += "&cmd=rankings";
+ url_rankings += "&host=" + encodeURIComponent(context1_["host"]);
+ url_rankings += "&platform=" +
+ encodeURIComponent(context1_["platform"]);
+ url_rankings += "&branch=" + encodeURIComponent(context1_["branch"]);
+ url_rankings += "&sha1=" + context1_["sha1"];
+ url_rankings += "&testcasefilter=" + testCaseFilter;
+ url_rankings += "&maxsize=" + $("#max_rank_size option:selected").val();
+ } else {
+ url_rankings = "";
+ }
+
+ // --- stats2 ---
+ if (context2_["host"] != "") {
+ url_stats2 = "stats2.html";
+ url_stats2 += "?db=" + $('#database').val();
+ url_stats2 += "&cmd=stats2";
+ url_stats2 += "&host1=" + encodeURIComponent(context1_["host"]);
+ url_stats2 += "&platform1=" + encodeURIComponent(context1_["platform"]);
+ url_stats2 += "&branch1=" + encodeURIComponent(context1_["branch"]);
+ url_stats2 += "&sha11=" + context1_["sha1"];
+ url_stats2 += "&host2=" + encodeURIComponent(context2_["host"]);
+ url_stats2 += "&platform2=" + encodeURIComponent(context2_["platform"]);
+ url_stats2 += "&branch2=" + encodeURIComponent(context2_["branch"]);
+ url_stats2 += "&sha12=" + context2_["sha1"];
+ url_stats2 += "&testcasefilter=" + testCaseFilter;
+ } else {
+ url_stats2 = "";
+ }
+
+ // --- tsstats ---
+ if ((context1_["host"] == context2_["host"]) &&
+ (context1_["platform"] == context2_["platform"]) &&
+ (context1_["branch"] == context2_["branch"]) &&
+ (context1_["sha1"] != context2_["sha1"])) {
+ url_tsstats = "tsstats.shtml";
+ url_tsstats += "?db=" + $('#database').val();
+ url_tsstats += "&cmd=timeseriesstats";
+ url_tsstats += "&host=" + encodeURIComponent(context1_["host"]);
+ url_tsstats += "&platform=" + encodeURIComponent(context1_["platform"]);
+ url_tsstats += "&branch=" + encodeURIComponent(context1_["branch"]);
+ url_tsstats += "&sha11=" + context1_["sha1"];
+ url_tsstats += "&sha12=" + context2_["sha1"];
+ url_tsstats += "&difftol=" + $("#diff_tol option:selected").val();
+ url_tsstats += "&durtolmin=" +
+ $("#dur_tol_min option:selected").val();
+ url_tsstats += "&durtolmax=" +
+ $("#dur_tol_max option:selected").val();
+ url_tsstats += "&testcasefilter=" + testCaseFilter;
+
+ var nsnapshots =
+ Math.abs(
+ parseInt(context1_["snapshotIndex"]) -
+ parseInt(context2_["snapshotIndex"]))
+ + 1;
+ $("#tsstats_nsnapshots").text(nsnapshots);
+ } else {
+ url_tsstats = "";
+ $("#tsstats_nsnapshots").text(0);
+ }
+
+ setActionUrl("action_stats1", url_stats1);
+ setActionUrl("action_rankings", url_rankings);
+ setActionUrl("action_stats2", url_stats2);
+ setActionUrl("action_tsstats", url_tsstats);
+}
+
+function updateSelectedSnapshotColor(select) {
+ sha1 = select.attr("value");
+ option = select.find("option[value = '" + sha1 + "']");
+ bgColor = option.css("backgroundColor");
+ select.css("backgroundColor", bgColor);
+}
+
+// Handles selection of a new snapshot for a context.
+function selectContextSnapshot(contextIndex) {
+ tr = $("#contextsTable tr:eq(" + (contextIndex + 1) + ")");
+ select = tr.find("select");
+ updateSelectedSnapshotColor(select);
+ updateActions();
+ fetchTestCases();
+}
+
+// Handles selection of a new snapshot from the duplicated list for context 2.
+function selectExtraContext2Snapshot(skipFetchTestCases) {
+ select = $("#extra_context2_snapshots select");
+ updateSelectedSnapshotColor(select);
+ updateActions();
+ if ((skipFetchTestCases == null) || (!skipFetchTestCases)) {
+ fetchTestCases();
+ }
+}
+
+// Returns the currently selected Context 1.
+function context1() {
+ context = [];
+
+ tr = $("#contextsTable tr:has(input[name='context1'][checked='true'])");
+ if (tr.length == 0)
+ return null;
+
+ context["host"] = tr.find("td[name = 'host']").text();
+ context["platform"] = tr.find("td[name = 'platform']").text();
+ context["branch"] = tr.find("td[name = 'branch']").text();
+
+ var snapshot = tr.find("select[name = 'snapshot'] option:selected");
+ context["sha1"] = snapshot.attr("value");
+ context["snapshotIndex"] = snapshot.index();
+ context["rankingsExist"] = (snapshot.attr("text")[0] == 'R');
+
+ return context;
+}
+
+// Returns the currently selected Context 2 if any.
+function context2() {
+ context = [];
+ tr_hpb = tr_sha1 = null;
+
+ trExtra = $("#extra_context2_row");
+ if (trExtra.length > 0) {
+ mainIndex = trExtra.attr("name");
+ tr_hpb = $("#" + "context_row\\:" + mainIndex);
+ tr_sha1 = trExtra;
+ } else {
+ tr_hpb = tr_sha1 =
+ $("#contextsTable tr:has(input[name='context2'][checked='true'])");
+ }
+
+ context["host"] = tr_hpb.find("td[name = 'host']").text();
+ context["platform"] = tr_hpb.find("td[name = 'platform']").text();
+ context["branch"] = tr_hpb.find("td[name = 'branch']").text();
+
+ var snapshot = tr_sha1.find("select[name = 'snapshot'] option:selected");
+ context["sha1"] = snapshot.attr("value");
+ context["snapshotIndex"] = snapshot.index();
+
+ return context;
+}
+
+function fetchContexts() {
+ updateStatus("fetching available contexts ...", true);
+
+ $("#div_noMatchingContexts").css("display", "none");
+
+ database = $('#database').val();
+
+ query = "?db=" + database;
+ query += "&cmd=contexts";
+ query += "&rankedonly=" + (rankedOnly ? 1 : 0);
+
+ url = "http://" + location.host + "/cgi-bin/getstatswrapper" + query;
+ //alert("url: >" + url + "<");
+
+ $.ajax({
+ url: url,
+ type: "GET",
+ dataType: "json",
+
+ success: function(data, textStatus, request) {
+ if (request.readyState == 4) {
+ if (request.status == 200) {
+
+ if (data.error != null) {
+ updateStatus(
+ "fetching available contexts ... failed: " +
+ data.error, false);
+ return
+ }
+
+ updateStatus("loading contexts ... done", false);
+ updateStatus("", false);
+
+ // Remove all rows below the header ...
+ $("#contextsTable tr:gt(0)").remove();
+
+ currTime = dateToTimestamp(new Date());
+
+ // Insert new rows ...
+ contexts = data.contexts;
+ html = "";
+ for (i = 0; i < contexts.length; ++i) {
+ context = contexts[i];
+ html += "<tr id=\"context_row:" + i + "\">";
+ html += "<td><input id=\"context1_cb:" + i + "\" " +
+ "name=\"context1\" type=\"radio\" " +
+ "onclick=\"clickContextRadioButton(this, 0)\">" +
+ "</td>";
+ html += "<td><input id=\"context2_cb:" + i + "\" " +
+ "name=\"context2\" type=\"radio\" " +
+ "onclick=\"clickContextRadioButton(this, 1)\">" +
+ "</td>";
+
+ html += "<td name=\"host\">" + context.host +
+ "</td>";
+ html += "<td name=\"platform\">" + context.platform +
+ "</td>";
+ html += "<td name=\"branch\">" + context.branch +
+ "</td>";
+
+ html += "<td>";
+
+ snapshots = context.snapshots;
+
+ firstUploadTime0 = snapshots[0][1];
+ secsAgo0 = currTime - firstUploadTime0;
+ ageColor0 = ageColor(secsAgo0);
+ html += "<select id=\"context_snapshots:" + i +
+ "\" name=\"snapshot\" style=\"" +
+ "background-color:" + ageColor0 +
+ "; font-family:lucida sans typewriter, " +
+ "courier new; width:100%; font-size:90%\" " +
+ "onchange=\"selectContextSnapshot(" + i + ")\">";
+ for (j = 0; j < snapshots.length; ++j) {
+ snapshot = snapshots[j];
+ sha1 = snapshot[0];
+ firstUploadTime = snapshot[1];
+ secsAgo = currTime - firstUploadTime;
+ ageColor_ = ageColor(secsAgo);
+ html += "<option style=\"" +
+ "background-color:" + ageColor_ +
+ "; font-family:lucida sans typewriter, " +
+ "courier new\" value=\"" + sha1 +
+ "\">" +
+ (snapshot[2] ? "R&nbsp;" : "&nbsp;&nbsp;") +
+ sha1 + "&nbsp;&nbsp;(first upload: " +
+ secsToDays(secsAgo) +
+ " days ago)</option>";
+ }
+ html += "</select>";
+ html +="</td>";
+
+ html += "</tr>";
+ }
+ $("#contextsTable > tbody:last").append(html);
+
+ // Select the first context as Context 1 ...
+ $("#contextsTable td:first input").attr("checked", "true");
+ if (context1() == null)
+ $("#div_noMatchingContexts").css("display", "block");
+
+ highlightContextsTable();
+ updateActions();
+
+ $("#div_availableContexts").css("display", "block");
+
+ if (rankedOnly) {
+ // In contexts table, disable the 'Context 2' column:
+ $("#contextsTable th:eq(1)").remove();
+ $("#contextsTable td:nth-child(2)").remove();
+ }
+
+ fetchTestCases();
+ }
+ }
+ },
+
+ error: function(request, textStatus, errorThrown) {
+ descr = errorThrown;
+ if (errorThrown == null) {
+ descr = "undefined error - is the server down?";
+ }
+ updateStatus(
+ "fetching available contexts ... error: " + descr, false);
+ }
+
+ // complete: function(request, textStatus) {
+ // alert("complete; request.status: " + request.status)
+ // }
+
+ });
+
+ return false;
+}
+
+// Handles selecting another database.
+function selectDatabase() {
+ fetchContexts();
+}
+
+function highlightContextsTable() {
+ // Unhighlight all selectable rows:
+ $("#contextsTable tr:has(input)").attr("class", "unselected");
+
+ // Highlight selected rows (prioritizing context 1):
+ $.each(["context2", "context1"], function(index, value) {
+ rows = $("#contextsTable tr:has(input[name='" + value +
+ "'][checked='true'])");
+ rows.attr("class", value);
+ });
+
+ // Highlight extra context 2 row (if any):
+ $("#extra_context2_row").attr("class", "context2");
+}
+
+// Handles clicking a context radio button.
+function clickContextRadioButton(cb, col) {
+ // Check if the same host/platform/branch combination is selected for
+ // both contexts. If so, add an extra row below the selected row to enable
+ // selection of a different snapshot for the Context 2
+ // (unless such an extra row doesn't already exist):
+ index = cb.id.split(":")[1];
+ tr = $("#" + "context_row\\:" + index);
+ checked1 = tr.find("td:nth-child(1) input").attr("checked");
+ checked2 = tr.find("td:nth-child(2) input").attr("checked");
+ if (checked1 && checked2) {
+ if ($("#extra_context2_row").length == 0) {
+
+ // Get selected sha1:
+ sha1 = tr.find("select option:selected").attr("value");
+
+ // Append extra row:
+ tr.after(
+ "<tr id=\"extra_context2_row\" name=\"" + index + "\">" +
+ "<td colspan=5> <td id=\"extra_context2_snapshots\">" +
+ "</td></tr>");
+ // Clone snapshots:
+ tr.find("#context_snapshots\\:" + index).clone().appendTo(
+ "#extra_context2_snapshots");
+ extraSelect = $("#extra_context2_snapshots select");
+ extraSelect.find("option[value = '" + sha1 + "']").attr(
+ "selected", true);
+ extraSelect.attr("onchange", ""); // This is needed!
+ extraSelect.change(function() {
+ selectExtraContext2Snapshot();
+ });
+
+ // Initialize selected snapshot color:
+ selectExtraContext2Snapshot(true);
+ }
+ } else {
+ // Remove any extra row:
+ trExtra = $("#extra_context2_row");
+ if (trExtra.length > 0) {
+
+ mainIndex = trExtra.attr("name");
+ trMain = $("#" + "context_row\\:" + mainIndex);
+
+ // If Context 2 is still selected for the main row,
+ // select the current snapshot of the main row as the one
+ // selected for the extra row before removing the latter:
+ if (trMain.find("td:nth-child(2) input").attr("checked")) {
+ // Get selected sha1 in extra row:
+ sha1 = trExtra.find("select option:selected").attr("value");
+
+ // Select this sha1 in the main row:
+ trMain.find("select option[value = '" + sha1 + "']").attr(
+ "selected", true);
+
+ // Initialize selected snapshot color:
+ selectContextSnapshot(parseInt(mainIndex));
+ }
+
+ trExtra.remove();
+ }
+
+ }
+
+ highlightContextsTable();
+ updateActions();
+ fetchTestCases();
+}
+
+$(document).ready(function() {
+
+ var args = queryStringArgs();
+
+ var rankedOnly_int = parseInt(extractArg(args, "rankedonly"));
+ rankedOnly = ((!isNaN(rankedOnly_int)) && (rankedOnly_int != 0))
+ if (rankedOnly) {
+ // In actions table, keep only header and rows related to
+ // 'Show rankings...' action:
+ $("#actionTable tr").css("display", "none");
+ $("#actionTable tr[id='actionTable_header']").css("display", "");
+ $("#actionTable tr[id*='rankings']").css("display", "");
+
+ $("#title").text("BM2 Analysis (ranked snapshots only)");
+
+ $("#div_analysisPageRankedOnlyLink").css("display", "none");
+ } else {
+ $("#div_analysisPageLink").css("display", "none");
+ }
+
+ // Set default action arguments:
+ $("#max_rank_size option[value='10']").attr("selected", true);
+ $("#diff_tol option[value='1.1']").attr("selected", true);
+ $("#dur_tol_min option[value='3']").attr("selected", true);
+ $("#dur_tol_max option[value='50']").attr("selected", true);
+
+ fetchContexts();
+});
diff --git a/web/analysis/rankings.js b/web/analysis/rankings.js
new file mode 100644
index 0000000..890165e
--- /dev/null
+++ b/web/analysis/rankings.js
@@ -0,0 +1,400 @@
+// --- BEGIN Global variables -----------------------------------
+var maxsize = null; // Maximum number of benchmarks in a ranking
+// --- END Global variables -------------------------------------
+
+function selectRankingTable() {
+ var val = $("#select_rankingTable").attr("value");
+ var types = ["qs", "lcssr", "lcssi", "lcss1r", "lcss1i"];
+ for (index in types) {
+ var type = types[index];
+ $("#div_rankingTable_" + type).css(
+ "display", (val == type) ? "block" : "none");
+ }
+
+ var currTable = "#rankingTable_" + val;
+
+ // Update "number of rows" label:
+ var nrows = $(currTable).find("tr").length - 1;
+ $("#rankingTable_nrows").text(
+ nrows + ((nrows == 1) ? " row" : " rows") +
+ " (limit: " + (maxsize < 0 ? "unlimited" : maxsize) + ")");
+
+ // Update plot to reflect selection of current table:
+ var cb = $(currTable).find("input[name='currSelector'][checked='true']");
+ if (cb.length == 0) {
+ if (plot)
+ clearPlot();
+ } else {
+ cb.trigger("click");
+ cb.attr("checked", true);
+ }
+}
+
+
+// Submits a note for a given time series.
+function submitTimeSeriesNote(
+ textInput, tableSel, database, host, platform, branch, benchmark, metric) {
+
+ updateStatus("updating note ...", true);
+
+ // Synchronize other ranking tables:
+ // ### WORK IN PROGRESS:
+ // var types = ["qs", "lcssr", "lcssi", "lcss1r", "lcss1i"];
+ // for (var i in types) {
+ // var tsel = "#rankingTable_" + types[i];
+ // if (tsel == tableSel)
+ // continue;
+ // alert("i: " + i + ", tsel: " + tsel);
+ // var tr = $(tsel + " tr").find(
+ // "td:nth-child(6):contains(" + metric + ")").find(
+ // "td:nth-child(7):contains(" + benchmark + ")");
+ // alert("tr.length: " + tr.length);
+ // }
+
+
+ // Update database:
+ query = "?db=" + database +
+ "&cmd=settimeseriesnote" +
+ "&host=" + encodeURIComponent(host) +
+ "&platform=" + encodeURIComponent(platform) +
+ "&branch=" + encodeURIComponent(branch) +
+ "&benchmark=" + encodeURIComponent(benchmark) +
+ "&metric=" + encodeURIComponent(metric) +
+ "&note=" + encodeURIComponent(textInput.value.substring(0, 256));
+
+ url = "http://" + location.host + "/cgi-bin/getstatswrapper" + query;
+ //alert("url: >" + url + "<");
+
+ $.ajax({
+ url: url,
+ type: "GET",
+ dataType: "json",
+
+ success: function(data, textStatus, request) {
+ if (request.readyState == 4) {
+ if (request.status == 200) {
+
+ if (data.error != null) {
+ updateStatus(
+ "updating note ... failed: " +
+ data.error, false);
+ return
+ }
+
+ updateStatus("updating note ... done", false);
+ updateStatus("", false);
+ }
+ }
+ },
+
+ error: function(request, textStatus, errorThrown) {
+ descr = errorThrown;
+ if (errorThrown == null) {
+ descr = "undefined error - is the server down?";
+ }
+ updateStatus("updating note ... error: " + descr, false);
+ }
+
+ // complete: function(request, textStatus) {
+ // alert("complete; request.status: " + request.status)
+ // }
+
+ });
+}
+
+// Submits a note for a given snapshot in a given time series.
+function submitSnapshotNote(textInput) {
+ // 2 B DONE!
+}
+
+function populateRankingTable(
+ tableSel, rankings, database, host, platform, branch, sha11, sha12,
+ difftol, durtolmin, durtolmax, bmarkId2Name, metricId2Name) {
+
+ // Remove all rows below the header ...
+ $(tableSel + " tr:gt(0)").remove();
+
+ var currTime = dateToTimestamp(currDate);
+
+ var html = "";
+ for (var i = 0; i < rankings.length; ++i) {
+
+ var row = rankings[i];
+ var bmarkId = row[0];
+ var metricId = row[1];
+ var context1Id = row[2]; // unused?
+ var pos = row[3];
+ var val = row[4];
+ var lcTimestamp = row[5];
+ var note = row[6];
+ var hasPrevDelta = (row.length > 7);
+ var prevDelta = hasPrevDelta ? row[7] : null;
+
+ benchmark = bmarkId2Name[bmarkId];
+ metric = metricId2Name[metricId];
+
+ html += "<tr>"
+
+ html += "<td><input type=\"radio\" name=\"currSelector\" onclick=\"" +
+ "clickBMRadioButton(this, '" + tableSel + "', '" +
+ database + "', '" + host + "', '" +
+ platform + "', '" + branch + "', '" +
+ sha11 + "', '" + sha12 + "', '" +
+ benchmark + "', '" + metric + "', " +
+ difftol + ", " + durtolmin + ", " + durtolmax +
+ ")\"></td>";
+
+ if (pos >= 0) {
+ html += "<td style=\"text-align:right\">" + pos + "</td>";
+ } else {
+ html += "<td style=\"text-align:center; color:red\">n/a</td>";
+ }
+ if (hasPrevDelta) {
+ html += "<td style=\"text-align:right\">" + prevDelta + "</td>";
+ } else {
+ html += "<td style=\"text-align:center; color:red\">n/a</td>";
+ }
+ if (val >= 0) {
+ html += "<td style=\"text-align:right\">" + val + "</td>";
+ } else {
+ html += "<td style=\"text-align:center; color:red\">n/a</td>";
+ }
+
+ if (lcTimestamp >= 0) {
+ var secsAgo = currTime - lcTimestamp;
+ lcDaysAgo = secsToDays(secsAgo);
+ html += "<td style=\"background-color:" + ageColor(secsAgo) +
+ "; text-align:right\">" + lcDaysAgo + "</td>";
+ } else {
+ html += "<td style=\"text-align:center; color:red\">n/a</td>";
+ }
+
+ html += "<td class=\"metric\">" + metric + "</td>";
+ html += "<td class=\"benchmark\" style=\"width:50%\">" +
+ benchmark + "</td>";
+
+ html += "<td style=\"width:50%\">" +
+ "<input type=\"text\" style=\"width:100%\" " +
+ "value=\"" + note + "\" " +
+ "onchange=\"submitTimeSeriesNote(this, '" +
+ tableSel + "', '" +
+ database + "', '" + host + "', '" +
+ platform + "', '" + branch + "', '" +
+ benchmark + "', '" + metric + "')\" /></td>";
+
+ html += "</tr>";
+ }
+
+ $(tableSel + " > tbody:last").append(html);
+ $(tableSel).trigger("update");
+ if (html != "") // hm ... why is this test necessary?
+ $(tableSel).trigger("appendCache");
+
+ // var sorting = [[11,1],[0,0]];
+ //$("table").trigger("sorton",[sorting]);
+}
+
+function fetchRankings(
+ database, host, platform, branch, sha1, testCaseFilter, maxsize) {
+ updateStatus("fetching rankings ...", true);
+
+ query = "?db=" + database +
+ "&cmd=rankings" +
+ "&host=" + encodeURIComponent(host) +
+ "&platform=" + encodeURIComponent(platform) +
+ "&branch=" + encodeURIComponent(branch) +
+ "&sha1=" + sha1 +
+ "&maxsize=" + maxsize;
+ if (testCaseFilter != "")
+ query += "&testcasefilter=" + encodeURIComponent(testCaseFilter);
+
+ url = "http://" + location.host + "/cgi-bin/getstatswrapper" + query;
+ //alert("url: >" + url + "<");
+
+ $.ajax({
+ url: url,
+ type: "GET",
+ dataType: "json",
+
+ success: function(data, textStatus, request) {
+ if (request.readyState == 4) {
+ if (request.status == 200) {
+
+ if (data.error != null) {
+ updateStatus(
+ "fetching rankings ... failed: " +
+ data.error, false);
+ return
+ }
+
+ updateStatus("fetching rankings ... done", false);
+ updateStatus("", false);
+
+ var sha11 = data.snapshots[0][0];
+ var sha12 = data.snapshots[data.snapshots.length - 1][0];
+
+ // ### The tolerance values should automatically be
+ // set to those hardcoded in finalizeresults.py!
+ // ... 2 B DONE!
+ difftol = 1.1;
+ durtolmin = 3;
+ durtolmax = 30;
+
+ // Show context ...
+ $("#main_context_database").text(data.database);
+ $("#main_context_host").text(data.host);
+ $("#main_context_platform").text(data.platform);
+ $("#main_context_branch").text(data.branch);
+ $("#main_context_sha11").text(sha11);
+ $("#main_context_sha12").text(sha12);
+ $("#main_context_difftol").text(difftol);
+ $("#main_context_durtolmin").text(durtolmin);
+ $("#main_context_durtolmax").text(durtolmax);
+
+ setSnapshots(data.snapshots);
+
+
+ var bmarkId2Name = [];
+ for (var i = 0; i < data.benchmarks.length; ++i) {
+ bmarkInfo = data.benchmarks[i];
+ bmarkId2Name[bmarkInfo[0]] = bmarkInfo[1];
+ }
+
+ var metricId2Name = [];
+ for (var i = 0; i < data.metrics.length; ++i) {
+ metricInfo = data.metrics[i];
+ metricId2Name[metricInfo[0]] = metricInfo[1];
+ }
+
+ var rankings = {
+ "qs": data.rankings.qs,
+ "lcssr": data.rankings.lcssr,
+ "lcssi": data.rankings.lcssi,
+ "lcss1r": data.rankings.lcss1r,
+ "lcss1i": data.rankings.lcss1i
+ };
+ for (key in rankings)
+ populateRankingTable(
+ "#rankingTable_" + key, rankings[key],
+ data.database, data.host, data.platform,
+ data.branch, sha11, sha12, difftol, durtolmin,
+ durtolmax, bmarkId2Name, metricId2Name);
+
+
+ // Initially show the QS statistic table:
+ $("#select_rankingTable").attr("value", "qs");
+ selectRankingTable();
+
+ $("#div_tsbm_border").css("display", "block");
+ $("#div_tsbm").css("display", "block");
+ $("#div_perBenchmarkStats").css("display", "block");
+
+ $("#div_context").css("display", "block");
+ $("#div_rankings").css("display", "block");
+
+ clearPlot();
+ }
+ }
+ },
+
+ error: function(request, textStatus, errorThrown) {
+ descr = errorThrown;
+ if (errorThrown == null) {
+ descr = "undefined error - is the server down?";
+ }
+ updateStatus("fetching rankings ... error: " + descr, false);
+ }
+
+ // complete: function(request, textStatus) {
+ // alert("complete; request.status: " + request.status)
+ // }
+
+ });
+}
+
+
+function initRankingTable(tableSel) {
+
+ $(tableSel).tablesorter({
+ headers: {
+ 0: { sorter: false }, // checkbox
+ 1: { sorter: "mixed_numeric_asc_before_missing" }, // Pos
+ 2: { sorter: "mixed_numeric_desc_before_missing" }, // Delta
+ 3: { sorter: false }, // Score (ordered as pos!)
+ 4: { sorter: "mixed_numeric_asc_before_missing" }, // LCDA
+ 5: { }, // Metric
+ 6: { } // Benchmark
+ }
+ });
+
+ // Note: The nth-child selector below uses 1-based indexing!
+ setTooltip( // Position
+ $(tableSel).find("th:nth-child(2)"),
+ "Ranking position. The lower the number, the stronger the " +
+ "benchmark deserves attention.");
+ setTooltip( // Delta
+ $(tableSel).find("th:nth-child(3)"),
+ "The previous ranking position minus the current one. " +
+ "The higher the number, the faster the benchmark rises " +
+ "in the ranking.");
+ setTooltip( // Score
+ $(tableSel).find("th:nth-child(4)"),
+ "The value of the current ranking statistic.");
+ setTooltip( // LCDA
+ $(tableSel).find("th:nth-child(5)"), tooltipText_lcda_nodist());
+}
+
+$(document).ready(function() {
+
+ initTablesorter();
+ initTSBMBody();
+
+ initRankingTable("#rankingTable_qs");
+ initRankingTable("#rankingTable_lcssr");
+ initRankingTable("#rankingTable_lcssi");
+ initRankingTable("#rankingTable_lcss1r");
+ initRankingTable("#rankingTable_lcss1i");
+
+ var args = queryStringArgs();
+
+ database = extractArg(args, "db");
+ if (database == "") {
+ alert("ERROR: invalid query string (empty database)");
+ return;
+ }
+
+ host = extractArg(args, "host");
+ if (host == "") {
+ alert("ERROR: invalid query string (empty host)");
+ return;
+ }
+ platform = extractArg(args, "platform");
+ if (platform == "") {
+ alert("ERROR: invalid query string (empty platform)");
+ return;
+ }
+ branch = extractArg(args, "branch");
+ if (branch == "") {
+ alert("ERROR: invalid query string (empty branch)");
+ return;
+ }
+ sha1 = extractArg(args, "sha1");
+ if (sha1 == "") {
+ alert("ERROR: invalid query string (empty sha1)");
+ return;
+ }
+ maxsize = extractArg(args, "maxsize");
+ if (maxsize == "") {
+ alert("ERROR: invalid query string (empty maxsize)");
+ return;
+ }
+
+ var testCaseFilter = extractArg(args, "testcasefilter"); // optional
+
+ $("#div_tsbm_border").css("display", "none");
+ $("#div_tsbm").css("display", "none");
+ $("#div_rankings").css("display", "none");
+
+ fetchRankings(
+ database, host, platform, branch, sha1, testCaseFilter, maxsize);
+});
diff --git a/web/analysis/rankings.shtml b/web/analysis/rankings.shtml
new file mode 100644
index 0000000..a8054f7
--- /dev/null
+++ b/web/analysis/rankings.shtml
@@ -0,0 +1,147 @@
+<html>
+
+<head>
+
+ <title>BM2 - Rankings</title>
+
+ <script type="text/javascript" src="jquery-1.4.2.min.js"></script>
+ <script type="text/javascript" src="flot/jquery.flot.js"></script>
+ <script type="text/javascript" src="flot/jquery.flot.selection.js"></script>
+ <script type="text/javascript" src="tablesorter/jquery.tablesorter.js">
+ </script>
+ <script type="text/javascript" src="boxover/boxover.js"></script>
+ <script type="text/javascript" src="global.js"></script>
+ <script type="text/javascript" src="tsbmbody.js"></script>
+ <script type="text/javascript" src="rankings.js"></script>
+
+ <link type="text/css" rel="stylesheet" href="style.css" />
+ <link type="text/css" rel="stylesheet" href="tablesorter/docs/css/jq.css" />
+ <link type="text/css" rel="stylesheet"
+ href="tablesorter/themes/bm/style.css" />
+
+</head>
+
+<body>
+
+<span id="title" style="font-size:18; font-weight:bold">
+BM2 - Rankings</span>
+&nbsp;&nbsp;&nbsp;
+<span style="white-space:nowrap">
+<span id="status1">no status</span>
+<img alt="spinner1" id="spinner1" src="images/ajax-spinner.gif"
+ style="display:none"/>
+<img alt="nospinner1" id="nospinner1" src="images/nospinner.png"
+ style="display:inline"/>
+</span>
+
+<br />
+
+<a id="mainPageLink" href="">main page</a>
+<script type="text/javascript">
+$("#mainPageLink").attr("href", "http://" + location.host + "/bm2");
+</script>
+
+&nbsp;&nbsp;
+
+<a id="analysisPageLink" href="">analysis page (all types)</a>
+<script type="text/javascript">
+$("#analysisPageLink").attr(
+ "href", "http://" + location.host + "/bm2/analysis");
+</script>
+
+&nbsp;&nbsp;
+
+<a id="analysisPageRankedOnlyLink" href="">
+ analysis page (ranked snapshots only)</a>
+<script type="text/javascript">
+$("#analysisPageRankedOnlyLink").attr(
+ "href", "http://" + location.host + "/bm2/analysis?rankedonly=1");
+</script>
+
+<br />
+<br />
+
+
+<div id="div_tsbm_border"
+ style="display:none; border-style:solid; border-width:2px; padding:5px">
+<!--#include file="tsbmbody.html" -->
+</div>
+
+
+<div id="div_rankings" style="display:none">
+
+<span id="status2">no status</span>
+<img alt="spinner2" id="spinner2" src="images/ajax-spinner.gif"
+ style="display:none"/>
+<img alt="nospinner2" id="nospinner2" src="images/nospinner.png"
+ style="display:inline"/>
+
+<!-- *** BEGIN Rankings *********************************** -->
+<br />
+<!-- <span style="font-size:14; font-weight:bold">Benchmarks:</span> -->
+<select id="select_rankingTable" onchange="selectRankingTable()">
+ <option value="qs" selected=1>Benchmark Quality</option>
+ <option value="lcssr">Last Change Stability (regressions only)</option>
+ <option value="lcssi">Last Change Stability (improvements only)</option>
+ <option value="lcss1r">
+ Last Change Stability (regressions only, include premature last changes)
+ </option>
+ <option value="lcss1i">
+ Last Change Stability (improvements only, include premature last changes)
+ </option>
+</select>
+<script type="text/javascript">
+ setTooltip($("#select_rankingTable"), "The current ranking statistic.")
+</script>
+
+<br />
+<span id="rankingTable_nrows">no rows</span>
+
+<br />
+<div style="overflow:auto; height:400px; border-style:solid; border-width:1px;">
+
+
+<div id="div_rankingTable_qs" style="display:block">
+<table id="rankingTable_qs" class="tablesorter" border="0" cellpadding="0"
+ cellspacing="1" style="width:100%">
+<!--#include file="rankingtabledef.html" -->
+</table>
+</div> <!-- div_rankingTable_qs -->
+
+<div id="div_rankingTable_lcssr" style="display:block">
+<table id="rankingTable_lcssr" class="tablesorter" border="0" cellpadding="0"
+ cellspacing="1" style="width:100%">
+<!--#include file="rankingtabledef.html" -->
+</table>
+</div> <!-- div_rankingTable_lcssr -->
+
+<div id="div_rankingTable_lcssi" style="display:block">
+<table id="rankingTable_lcssi" class="tablesorter" border="0" cellpadding="0"
+ cellspacing="1" style="width:100%">
+<!--#include file="rankingtabledef.html" -->
+</table>
+</div> <!-- div_rankingTable_lcssi -->
+
+<div id="div_rankingTable_lcss1r" style="display:block">
+<table id="rankingTable_lcss1r" class="tablesorter" border="0" cellpadding="0"
+ cellspacing="1" style="width:100%">
+<!--#include file="rankingtabledef.html" -->
+</table>
+</div> <!-- div_rankingTable_lcss1r -->
+
+<div id="div_rankingTable_lcss1i" style="display:block">
+<table id="rankingTable_lcss1i" class="tablesorter" border="0" cellpadding="0"
+ cellspacing="1" style="width:100%">
+<!--#include file="rankingtabledef.html" -->
+</table>
+</div> <!-- div_rankingTable_lcss1i -->
+
+
+</div>
+<!-- *** END Rankings *********************************** -->
+
+</div> <!-- div_rankings -->
+
+</body>
+
+</html>
diff --git a/web/analysis/rankingtabledef.html b/web/analysis/rankingtabledef.html
new file mode 100644
index 0000000..6eb662b
--- /dev/null
+++ b/web/analysis/rankingtabledef.html
@@ -0,0 +1,14 @@
+<thead>
+<tr>
+ <th></th> <!-- check box -->
+ <th>Pos</th>
+ <th>Delta</th>
+ <th>Score</th>
+ <th>LCDA</th>
+ <th>Metric</th>
+ <th>Benchmark</th>
+ <th>Note</th>
+</tr>
+</thead>
+<tbody>
+</tbody>
diff --git a/web/analysis/stats1.html b/web/analysis/stats1.html
new file mode 100644
index 0000000..69ce7e8
--- /dev/null
+++ b/web/analysis/stats1.html
@@ -0,0 +1,159 @@
+<html>
+
+<head>
+
+ <title>BM2 - Context Details</title>
+
+ <script type="text/javascript" src="jquery-1.4.2.min.js"></script>
+ <script type="text/javascript" src="tablesorter/jquery.tablesorter.js">
+ </script>
+ <script type="text/javascript" src="boxover/boxover.js"></script>
+ <script type="text/javascript" src="global.js"></script>
+ <script type="text/javascript" src="stats1.js"></script>
+
+ <link type="text/css" rel="stylesheet" href="style.css" />
+ <link type="text/css" rel="stylesheet" href="tablesorter/docs/css/jq.css" />
+ <link type="text/css" rel="stylesheet"
+ href="tablesorter/themes/bm/style.css" />
+
+</head>
+
+<body>
+
+<span id="title" style="font-size:18; font-weight:bold">
+BM2 - Context Details</span>
+&nbsp;&nbsp;&nbsp;
+<span style="white-space:nowrap">
+<span id="status">no status</span>
+<img alt="spinner" id="spinner" src="images/ajax-spinner.gif"
+ style="display:none"/>
+</span>
+
+<br />
+
+<a id="mainPageLink" href="">main page</a>
+<script type="text/javascript">
+$("#mainPageLink").attr("href", "http://" + location.host + "/bm2");
+</script>
+
+&nbsp;&nbsp;
+
+<a id="analysisPageLink" href="">analysis page (all types)</a>
+<script type="text/javascript">
+$("#analysisPageLink").attr(
+ "href", "http://" + location.host + "/bm2/analysis");
+</script>
+
+<br />
+<br />
+
+<!-- *** BEGIN Shown context *********************************** -->
+<div id="div_shownContext" style="display:none">
+<span style="font-size:14; font-weight:bold">Context:</span>
+<table>
+<tbody>
+<tr><td style="text-align:right"><b>Database:</b></td>
+ <td id="shown_database" colspan=2>none</td></tr>
+<tr><td style="text-align:right"><b>Host:</b></td>
+ <td id="shown_host">none</td></tr>
+<tr><td style="text-align:right"><b>Platform:</b></td>
+ <td id="shown_platform">none</td></tr>
+<tr><td style="text-align:right"><b>Branch:</b></td>
+ <td id="shown_branch">none</td></tr>
+<tr><td style="text-align:right"><b>SHA-1:</b></td>
+ <td id="shown_sha1">none</td></tr>
+</tbody>
+</table>
+</div>
+<!-- *** END Shown context *********************************** -->
+
+<br />
+
+
+<div id="div_statistics" style="display:none">
+
+<!-- Overall stats: ... 2 B DONE! -->
+<span style="font-size:14; font-weight:bold">Overall statistics:
+<span style="color:red"> (2 B DONE!)</span>
+</span>
+<br />
+<br />
+
+
+<!-- *** BEGIN Per-benchmark stats *********************************** -->
+<br />
+<span style="font-size:14; font-weight:bold">Benchmarks:</span>
+<br />
+<span id="pbmTable_nrows">no</span> rows
+<span id="pbmTable_sortInProgress"
+ style="display:none">&nbsp;&nbsp;(sorting ...)</span>
+
+<br />
+<div style="overflow:auto; height:800px; border-style:solid; border-width:1px;">
+
+<table id="pbmTable" class="tablesorter" border="0"
+ cellpadding="0" cellspacing="1">
+<thead>
+<tr>
+ <th id="ntotal">Total</th>
+ <script type="text/javascript">
+ setTooltip($("#ntotal"), "Sample size (# of observations)");
+ </script>
+ <th id="nvalid">Valid</th>
+ <script type="text/javascript">
+ setTooltip($("#nvalid"), "Valid sample size (# of valid observations)");
+ </script>
+ <th id="min">Min</th>
+ <script type="text/javascript">
+ setTooltip($("#min"), "Lowest valid observation");
+ </script>
+ <th id="max">Max</th>
+ <script type="text/javascript">
+ setTooltip($("#max"), "Highest valid observation");
+ </script>
+ <th id="median">Median</th>
+ <script type="text/javascript">
+ setTooltip($("#median"), "Median of valid observations");
+ </script>
+ <th id="mean">Mean</th>
+ <script type="text/javascript">
+ setTooltip($("#mean"), "Arithmetic mean of valid observations");
+ </script>
+ <th id="stddev">Stddev</th>
+ <script type="text/javascript">
+ setTooltip($("#stddev"), "Standard deviation of valid observations");
+ </script>
+ <th id="rsd">RSD</th>
+ <script type="text/javascript">
+ setTooltip(
+ $("#rsd"), "Relative standard deviation of valid observations");
+ </script>
+ <th id="rse">RSE</th>
+ <script type="text/javascript">
+ setTooltip($("#rse"), "Relative standard error of valid observations");
+ </script>
+ <th>H0</th> <!-- histogram bin 0 -->
+ <th>H1</th>
+ <th>H2</th>
+ <th>H3</th>
+ <th>H4</th>
+ <th>H5</th>
+ <th>H6</th>
+ <th>H7</th>
+ <th>H8</th>
+ <th>H9</th> <!-- histogram bin 9 -->
+ <th>Metric</th>
+ <th>Benchmark</th>
+</tr>
+</thead>
+<tbody>
+</tbody>
+</table>
+</div>
+<!-- *** END Per-benchmark stats *********************************** -->
+
+</div>
+
+</body>
+
+</html>
diff --git a/web/analysis/stats1.js b/web/analysis/stats1.js
new file mode 100644
index 0000000..758b092
--- /dev/null
+++ b/web/analysis/stats1.js
@@ -0,0 +1,215 @@
+function fetchStats(database, host, platform, branch, sha1, testCaseFilter) {
+ updateStatus("fetching statistics ...", true);
+
+ query = "?db=" + database +
+ "&cmd=stats1" +
+ "&host=" + encodeURIComponent(host) +
+ "&platform=" + encodeURIComponent(platform) +
+ "&branch=" + encodeURIComponent(branch) +
+ "&sha1=" + sha1;
+ if (testCaseFilter != "") {
+ query += "&testcasefilter=" + testCaseFilter;
+ }
+
+ url = "http://" + location.host + "/cgi-bin/getstatswrapper" + query;
+ // alert("url: >" + url + "<");
+
+ $.ajax({
+ url: url,
+ type: "GET",
+ dataType: "json",
+
+ success: function(data, textStatus, request) {
+ if (request.readyState == 4) {
+ if (request.status == 200) {
+
+ if (data.error != null) {
+ updateStatus(
+ "fetching statistics ... failed: " +
+ data.error, false);
+ return
+ }
+
+ updateStatus("fetching statistics ... done", false);
+ updateStatus("", false);
+
+ // Show context ...
+ $("#shown_database").text(data.database);
+ $("#shown_host").text(data.host);
+ $("#shown_platform").text(data.platform);
+ $("#shown_branch").text(data.branch);
+ $("#shown_sha1").text(data.sha1);
+
+ // --- BEGIN Populate overall stats table ---
+ // 2 B DONE!
+ // --- END Populate overall stats table ---
+
+
+ // --- BEGIN Populate per-benchmark stats table ---
+
+ // Remove all rows below the header ...
+ $("#pbmTable tr:gt(0)").remove();
+
+ pbm_stats = data.per_bm_stats;
+
+ // Show # of per-benchmark rows ...
+ $("#pbmTable_nrows").text(pbm_stats.length);
+
+ // Insert new rows ...
+ html = "";
+ for (i = 0; i < pbm_stats.length; ++i) {
+ row = pbm_stats[i];
+ html += "<tr>";
+ for (j = 0; j < row.length; ++j) {
+
+ if (j > 8 && j < 19) {
+ if (row[j] < 0) {
+ for (k = 0; k < 10; ++k) {
+ html +=
+ "<td style=\"color:#ff0000\">n/a</td>";
+ }
+ j = 18;
+ } else {
+ html +=
+ "<td " +
+ "style=\"text-align:right\">" +
+ row[j] + "</td>";
+ }
+ } else if (j <= 8) {
+ if (row[j] != "") {
+ html +=
+ "<td style=\"text-align:right\">" +
+ row[j] + "</td>";
+ } else {
+ html += "<td></td>";
+ }
+ } else if (j == 19) {
+ html += "<td class=\"metric\">" + row[j] +
+ "</td>";
+ } else if (j > 19) {
+ html += "<td class=\"benchmark\">" + row[j] +
+ "</td>";
+ } else {
+ html += "<td>" + row[j] + "</td>";
+ }
+ }
+ html += "</tr>";
+ }
+ $("#pbmTable > tbody:last").append(html);
+ $("#pbmTable").trigger("update");
+ $("#pbmTable").trigger("appendCache");
+
+ // var sorting = [[11,1],[0,0]];
+ //$("table").trigger("sorton",[sorting]);
+
+ // --- END Populate per-benchmark stats table ---
+
+
+ $("#div_shownContext").css("display", "block");
+ $("#div_statistics").css("display", "block");
+ }
+ }
+ },
+
+ error: function(request, textStatus, errorThrown) {
+ descr = errorThrown;
+ if (errorThrown == null) {
+ descr = "undefined error - is the server down?";
+ }
+ updateStatus("fetching statistics ... error: " + descr, false);
+ }
+
+ // complete: function(request, textStatus) {
+ // alert("complete; request.status: " + request.status)
+ // }
+
+ });
+}
+
+$(document).ready(function() {
+
+ $.tablesorter.addParser({
+ id: "mixed_numeric",
+ is: function(s) {
+ return false; // so this parser is not auto detected
+ },
+ format: function(s) {
+ f = parseFloat(s);
+ if (isNaN(f)) { return ""; }
+ return f.toFixed(20); // max precision guaranteed by ECMA standard
+ },
+ type: "numeric"
+ });
+
+ $("#pbmTable").tablesorter({
+ headers: {
+ // disable sorting:
+ // 9: { sorter: false }, // histogram
+
+ // handle proper sorting of mixed standard- and scientific notation:
+ 2: { sorter: "mixed_numeric" }, // min
+ 3: { sorter: "mixed_numeric" }, // max
+ 4: { sorter: "mixed_numeric" }, // median
+ 5: { sorter: "mixed_numeric" }, // mean
+ 6: { sorter: "mixed_numeric" }, // standard deviation
+ 7: { sorter: "mixed_numeric" }, // relative standard deviation
+ 8: { sorter: "mixed_numeric" }, // relative standard error
+ 9: { sorter: "mixed_numeric" }, // relative standard error
+ 10: { sorter: "mixed_numeric" }, // H0
+ 11: { sorter: "mixed_numeric" }, // H1
+ 12: { sorter: "mixed_numeric" }, // H2
+ 13: { sorter: "mixed_numeric" }, // H3
+ 14: { sorter: "mixed_numeric" }, // H4
+ 15: { sorter: "mixed_numeric" }, // H5
+ 16: { sorter: "mixed_numeric" }, // H6
+ 17: { sorter: "mixed_numeric" }, // H7
+ 18: { sorter: "mixed_numeric" }, // H8
+ 19: { sorter: "mixed_numeric" } // H9
+ }
+ });
+
+ $("#pbmTable").bind("sortStart",function() {
+ $("#pbmTable_sortInProgress").show();
+ }).bind("sortEnd",function() {
+ $("#pbmTable_sortInProgress").hide();
+ });
+
+ args = queryStringArgs();
+
+ // Extract database (required):
+ database = extractArg(args, "db");
+ if (database == "") {
+ alert("ERROR: invalid query string (empty database)");
+ return;
+ }
+
+ // Extract context (required):
+ host = extractArg(args, "host");
+ if (host == "") {
+ alert("ERROR: invalid query string (empty host)");
+ return;
+ }
+ platform = extractArg(args, "platform");
+ if (platform == "") {
+ alert("ERROR: invalid query string (empty platform)");
+ return;
+ }
+ branch = extractArg(args, "branch");
+ if (branch == "") {
+ alert("ERROR: invalid query string (empty branch)");
+ return;
+ }
+ sha1 = extractArg(args, "sha1");
+ if (sha1 == "") {
+ alert("ERROR: invalid query string (empty sha1)");
+ return;
+ }
+
+ // Extract test case filter (optional);
+ testCaseFilter = extractArg(args, "testcasefilter");
+
+ $("#div_shownContext").css("display", "none");
+ $("#div_statistics").css("display", "none");
+
+ fetchStats(database, host, platform, branch, sha1, testCaseFilter);
+});
diff --git a/web/analysis/stats2.html b/web/analysis/stats2.html
new file mode 100644
index 0000000..09cc523
--- /dev/null
+++ b/web/analysis/stats2.html
@@ -0,0 +1,485 @@
+<html>
+
+<head>
+
+ <title>BM2 - Context 1 vs Context 2</title>
+
+ <script type="text/javascript" src="jquery-1.4.2.min.js"></script>
+ <script type="text/javascript" src="flot/jquery.flot.js"></script>
+ <script type="text/javascript" src="flot/jquery.flot.selection.js"></script>
+ <script type="text/javascript" src="tablesorter/jquery.tablesorter.js">
+ </script>
+ <script type="text/javascript" src="boxover/boxover.js"></script>
+ <script type="text/javascript" src="global.js"></script>
+ <script type="text/javascript" src="stats2.js"></script>
+
+ <link type="text/css" rel="stylesheet" href="style.css" />
+ <link type="text/css" rel="stylesheet" href="tablesorter/docs/css/jq.css" />
+ <link type="text/css" rel="stylesheet"
+ href="tablesorter/themes/bm/style.css" />
+
+</head>
+
+<body>
+
+<span id="title" style="font-size:18; font-weight:bold">
+BM2 - Context 1 vs Context 2</span>
+&nbsp;&nbsp;&nbsp;
+<span style="white-space:nowrap">
+<span id="status">no status</span>
+<img alt="spinner" id="spinner" src="images/ajax-spinner.gif"
+ style="display:none"/>
+</span>
+
+<br />
+
+<a id="mainPageLink" href="">main page</a>
+<script type="text/javascript">
+$("#mainPageLink").attr("href", "http://" + location.host + "/bm2");
+</script>
+
+&nbsp;&nbsp;
+
+<a id="analysisPageLink" href="">analysis page (all types)</a>
+<script type="text/javascript">
+$("#analysisPageLink").attr(
+ "href", "http://" + location.host + "/bm2/analysis");
+</script>
+
+<br />
+<br />
+
+
+<div id="div_results" style="display:none">
+
+<!-- *** BEGIN Shown contexts *********************************** -->
+<!-- <span style="font-size:14; font-weight:bold">Shown contexts:</span> -->
+<span style="font-size:14; font-weight:bold">Context:</span>
+<table>
+<tbody>
+<tr><td style="text-align:right"><b>Database:</b></td>
+ <td id="shown_database" colspan=2>none</td></tr>
+<tr><td></td>
+ <td class="context1"><b>Context 1</b></td>
+ <td class="context2"><b>Context 2</b></td></tr>
+<tr><td style="text-align:right"><b>Host:</b></td>
+ <td id="shown_host1">none</td><td id="shown_host2">none</td></tr>
+<tr><td style="text-align:right"><b>Platform:</b></td>
+ <td id="shown_platform1">none</td><td id="shown_platform2">none</td></tr>
+<tr><td style="text-align:right"><b>Branch:</b></td>
+ <td id="shown_branch1">none</td><td id="shown_branch2">none</td></tr>
+<tr><td style="text-align:right"><b>SHA-1:</b></td>
+ <td id="shown_sha11">none</td><td id="shown_sha12">none</td></tr>
+<tr><td colspan=2></td>
+ <td id="context2_descr" style="font-size:20px"></td></tr>
+ <script type="text/javascript">
+ setTooltip(
+ $("#context2_descr"),
+ "Arithmetic mean of the individual median improvements " +
+ "from Context 1 to Context 2.<br />" +
+ "The values 0.5, 1, and 2 indicate half, equal, and double " +
+ "performance respectively.");
+ </script>
+</tbody>
+</table>
+<!-- *** END Shown contexts *********************************** -->
+
+<br />
+
+<!-- *** BEGIN Plot and overall stats ********************** -->
+<table style="border:0">
+<tr>
+
+<!-- *** BEGIN Plot **************************************** -->
+<td style="border:0" valign=top>
+<table style="border:0; width:400px">
+<tbody>
+ <tr>
+ <td style="border:0" valign=top>
+ <span style="font-size:14; font-weight:bold">Median plot
+ (<span id="plot_size">no</span> results):</span>
+ <br />(<span style="color:#0a0"><b>Green</b></span> values indicate better
+ performance in Context 2.
+ <br /><span style="color:#a00"><b>Red</b></span> values indicate better
+ performance in Context 1.
+ <br /><span style="color:#555"><b>Gray</b></span> values indicate less
+ than ca. 10% difference.
+ <br />Drag to zoom and hover/click for details.)
+ </td>
+ <td style="border:0; padding-right:0">
+ <div id="plot_overview_canvas_medians"
+ style="width:80px; height:80px; outline-width:1px; outline-style:solid;
+ background-color:#eee"
+ ondblclick="zoomOutMedianPlot()">
+ foo
+ </div>
+ </td>
+ </tr>
+ <tr>
+ <td style="border:0" colspan=2>
+ <div id="plot_canvas_medians"
+ style="width:400px; height:400px; outline-width:1px;
+ outline-style:solid; background-color:#eee"
+ ondblclick="zoomOutMedianPlot()">
+ no plot
+ </div>
+ <a href="javascript::void(0)" style="float:right"
+ onclick="zoomOutMedianPlot(); return false;">reset zoom</a>
+ </td>
+ </tr>
+</tbody>
+</table>
+</td>
+<!-- *** END Plot **************************************** -->
+
+
+<!-- *** BEGIN Individual observations ******************************* -->
+<td valign=top style="border:0; padding-left:20">
+<table style="border:0">
+<tbody>
+<tr>
+
+<td valign=top style="border:0">
+<span class="context1" style="font-size:12; font-weight:bold">Sample 1:</span>
+<table id="sample1" class="tablesorter" border="0"
+cellpadding="0" cellspacing="1" style="width:150px">
+ <thead>
+ <tr>
+ <th id="sample1_value">Value</th>
+ <script type="text/javascript">
+ setTooltip(
+ $("#sample1_value"),
+ "Individual observations in Context 1.<br />" +
+ "The median observation is shown in " +
+ "<span style=\"background-color:#ff0\">" +
+ "yellow</span>.<br />" +
+ "Invalid observations are shown in " +
+ "<span style=\"background-color:#faa\">red</span>.");
+ </script>
+ <th id="sample1_daysAgo">Days&nbsp;ago</th>
+ <script type="text/javascript">
+ setTooltip(
+ $("#sample1_daysAgo"),
+ "Days ago since observation was uploaded to the database.");
+ </script>
+ </tr>
+ </thead>
+ <tbody>
+ </tbody>
+</table>
+</td>
+
+<td valign=top style="border:0">
+<span class="context2" style="font-size:12; font-weight:bold">Sample 2:</span>
+<table id="sample2" class="tablesorter" border="0"
+ cellpadding="0" cellspacing="1" style="width:150px">
+ <thead>
+ <tr>
+ <th id="sample2_value">Value</th>
+ <script type="text/javascript">
+ setTooltip(
+ $("#sample2_value"),
+ "Individual observations in Context 2.<br />" +
+ "The median observation is shown in " +
+ "<span style=\"background-color:#ff0\">" +
+ "yellow</span>.<br />" +
+ "Invalid observations are shown in " +
+ "<span style=\"background-color:#faa\">red</span>.");
+ </script>
+ <th id="sample2_daysAgo">Days&nbsp;ago</th>
+ <script type="text/javascript">
+ setTooltip(
+ $("#sample2_daysAgo"),
+ "Days ago since observation was uploaded to the database.");
+ </script>
+ </tr>
+ </thead>
+ <tbody>
+ </tbody>
+</table>
+</td>
+
+</tr>
+</tbody>
+</table>
+</td>
+<!-- *** END Individual observations ******************************* -->
+
+
+<!-- *** BEGIN Overall stats **************************************** -->
+<td valign=top style="border:0; padding-left:20">
+
+<table style="border:0">
+<tbody>
+<tr>
+
+<td valign=top style="border:0">
+
+<span style="font-size:12; font-weight:bold">Common results:</span>
+<table>
+<tbody>
+<tr><td style="text-align:right"><b>Benchmarks:</b></td>
+ <td colspan=4 id="nbenchmarks" style="text-align:right">none</td></tr>
+<script type="text/javascript">
+ setTooltip(
+ $("#nbenchmarks"),
+ "Number of (benchmark, and metric) combinations common to " +
+ "Context 1 and 2.");
+</script>
+
+<tr><td style="text-align:right"><b>Test cases:</b></td>
+ <td colspan=4 id="ntestcases" style="text-align:right">none</td></tr>
+<script type="text/javascript">
+ setTooltip(
+ $("#ntestcases"),
+ "Number of distinct test cases common to Context 1 and 2.");
+</script>
+
+<tr><td style="text-align:right"><b>Valid medians:</b></td>
+ <td colspan=4 id="nmedian_improvs" style="text-align:right">none</td></tr>
+<script type="text/javascript">
+ setTooltip(
+ $("#nmedian_improvs"),
+ "Number of benchmarks for which the median observation is positive " +
+ "in both contexts.<br /><b>Note:</b> all median stats are " +
+ "based on these results only.");
+</script>
+</tbody>
+</table>
+
+</td>
+
+</tr>
+</tbody>
+</table>
+
+<br />
+<span id="medianImprovFact_overallStats" style="font-size:12; font-weight:bold">
+ Median improvement factors<br />from Context 1 to 2:</span>
+<script type="text/javascript">
+ setTooltip(
+ $("#medianImprovFact_overallStats"),
+ "Overall stats for the improvement factors of median " +
+ "observations from Context 1 to 2.<br />" +
+ "The values 0.5, 1, and 2 indicate half, equal, " +
+ "and double performance respectively.");
+</script>
+<table>
+<tbody>
+<tr>
+ <td colspan=2 style="background-color:#eee"><b>Benchmark count:</b></td>
+</tr>
+
+<tr><td style="text-align:right; color:#0a0">
+ <b>&gt;&nbsp;1.1 X better:</b></td>
+ <td id="better_than_fact_1_1_count" style="text-align:right">none</td>
+<script type="text/javascript">
+ setTooltip(
+ $("#better_than_fact_1_1_count"),
+ "Number of benchmarks whose median observation improved " +
+ "by a factor of more than 1.1 from Context 1 to Context 2.");
+</script>
+</tr>
+<tr><td style="text-align:right; color:#a00">
+ <b>&lt;&nbsp;1/1.1 X better:</b></td>
+ <td id="worse_than_fact_inv_1_1_count" style="text-align:right">none</td>
+<script type="text/javascript">
+ setTooltip(
+ $("#worse_than_fact_inv_1_1_count"),
+ "Number of benchmarks whose median observation improved " +
+ "by a factor of less than 1/1.1 from Context 1 to Context 2.");
+</script>
+</tr>
+<tr><td style="text-align:right"><b>No significant change:</b></td>
+ <td id="equal_count" style="text-align:right">none</td>
+<script type="text/javascript">
+ setTooltip(
+ $("#equal_count"),
+ "Number of benchmarks whose median observation improved<br />" +
+ "by a factor of more than 1/1.1 and less than 1.1 from " +
+ "Context 1 to Context 2.");
+</script>
+</tr>
+
+
+<tr>
+ <td colspan=2 style="background-color:#eee"><b>Distribution:</b></td>
+</tr>
+<tr><td style="text-align:right"><b>Mean:</b></td>
+ <td id="mean_of_median_improvs"></td>
+</tr>
+
+<tr><td colspan=5><b>Percentiles:</b></td></tr>
+
+<tr><td style="text-align:right"><b>90th:</b></td>
+ <td id="perc_90_of_median_improvs"></td>
+</tr>
+<tr><td style="text-align:right"><b>75th:</b></td>
+ <td id="perc_75_of_median_improvs"></td>
+</tr>
+<tr><td style="text-align:right"><b>50th:</b></td>
+ <td id="perc_50_of_median_improvs"></td>
+</tr>
+<tr><td style="text-align:right"><b>25th:</b></td>
+ <td id="perc_25_of_median_improvs"></td>
+</tr>
+<tr><td style="text-align:right"><b>10th:</b></td>
+ <td id="perc_10_of_median_improvs"></td>
+</tr>
+</tbody>
+</table>
+
+</td>
+<!-- *** END Overall stats **************************************** -->
+
+</tr>
+</table>
+<!-- *** END Plot and overall stats ********************** -->
+
+
+<!-- *** BEGIN Plot details (of selected result and hovered pos) ******* -->
+<table style="border:0">
+<tbody>
+<tr>
+<td style="border:0; padding:2; text-align:right" class="benchmark">
+ <b>Benchmark:</b></td>
+<td style="border:0; padding:2"><span id="plot_benchmark"></span></td>
+</tr>
+<tr>
+<td style="border:0; padding:2; text-align:right" class="metric">
+ <b>Metric:</b></td>
+<td style="border:0; padding:2"><span id="plot_metric"></span></td>
+</tr>
+
+<tr>
+<td id="plot_lead_median1" style="border:0; padding:2; text-align:right"
+ class="context1"><b>Value 1:</b></td>
+<script type="text/javascript">
+ setTooltip(
+ $("#plot_lead_median1"),
+ "Median observation in Context 1");
+</script>
+<td style="border:0; padding:2">
+ <span id="plot_median1"></span>
+ <span id="plot_median1_descr"></span>
+ <span id="plot_hoverMedian1" style="color:#888"></span>
+ <span id="plot_hoverMedian1_descr"></span>
+ <span id="plot_hoverMedian1_rightParen" style="color:#888"></span>
+</td>
+</tr>
+
+<tr>
+<td id="plot_lead_median2" style="border:0; padding:2; text-align:right"
+ class="context2"><b>Value 2:</b></td>
+<script type="text/javascript">
+ setTooltip(
+ $("#plot_lead_median2"),
+ "Median observation in Context 2");
+</script>
+<td style="border:0; padding:2">
+ <span id="plot_median2"></span>
+ <span id="plot_median2_descr"></span>
+ <span id="plot_hoverMedian2" style="color:#888"></span>
+ <span id="plot_hoverMedian2_descr"></span>
+ <span id="plot_hoverMedian2_rightParen" style="color:#888"></span>
+</td>
+
+</tr>
+</tbody>
+</table>
+<!-- *** END Plot details (of selected result and hovered pos) ******* -->
+
+<br />
+
+
+<!-- *** BEGIN Per-benchmark stats *********************************** -->
+<span style="font-size:14; font-weight:bold">Benchmarks:</span>
+<br />
+<br />
+<span id="pbmTable_nrows">no</span> rows
+<span id="pbmTable_sortInProgress"
+ style="display:none">&nbsp;&nbsp;(sorting ...)</span>
+
+<br />
+<div style="overflow:auto; height:200px; border-style:solid; border-width:1px;">
+
+<table id="pbmTable" class="tablesorter" border="0"
+ cellpadding="0" cellspacing="1">
+<thead>
+<tr>
+ <th></th>
+ <th id="median1">Median&nbsp;1</th>
+ <script type="text/javascript">
+ setTooltip($("#median1"), "Median observation in Context 1");
+ </script>
+ <th id="median2">Median&nbsp;2</th>
+ <script type="text/javascript">
+ setTooltip($("#median2"), "Median observation in Context 2");
+ </script>
+ <th id="pbmTable_medianImprovFact">Improv.</th>
+ <script type="text/javascript">
+ setTooltip(
+ $("#pbmTable_medianImprovFact"),
+ "Improvement factor of median observation from Context 1 to 2." +
+ "<br />The values 0.5, 1, and 2 indicate half, equal, " +
+ "and double performance respectively.<br />" +
+ "<span style=\"color:#080\">Green</span> values indicate " +
+ "factors better than 1.1.<br />" +
+ "<span style=\"color:#a00\">Red</span> values indicate " +
+ "factors worse than 1/1.1.");
+ </script>
+
+ <th id="rse1">RSE1</th>
+ <script type="text/javascript">
+ setTooltip(
+ $("#rse1"),
+ "Relative standard error in Context 1.<br />" +
+ "The values 0 and 100 indicate minimum and " +
+ "maximum dispersion with 100 being an asymptote." +
+ "<br /><br /><img src=\"images/rse.png\" />");
+ </script>
+ <th id="rse2">RSE2</th>
+ <script type="text/javascript">
+ setTooltip(
+ $("#rse2"),
+ "Relative standard error in Context 1.<br />" +
+ "The values 0 and 100 indicate minimum and " +
+ "maximum dispersion with 100 being an asymptote." +
+ "<br /><br /><img src=\"images/rse.png\" />");
+ </script>
+ <th id="pbmTable_rseImprovDiff">Improv.</th>
+ <script type="text/javascript">
+ setTooltip(
+ $("#pbmTable_rseImprovDiff"),
+ "Improvement difference of relative standard " +
+ "error from Context 1 to 2.<br />" +
+ "The values -1, 0, and 1 indicate maximum regression, " +
+ "no change, and maximum improvement respectively.<br />" +
+ "<span style=\"color:#080\">Green</span> values indicate " +
+ "regressions worse than -0.1.<br />" +
+ "<span style=\"color:#a00\">Red</span> values indicate " +
+ "improvements better than 0.1.");
+ </script>
+
+ <th id="metric">Metric</th>
+ <script type="text/javascript">
+ setTooltip(
+ $("#metric"),
+ "The quantity used for measuring the benchmark.<br />" +
+ "Classified into either \"lower is better\" or \"higher is better.\"");
+ </script>
+ <th>Benchmark</th>
+</tr>
+</thead>
+<tbody>
+</tbody>
+</table>
+</div>
+<!-- *** END Per-benchmark stats *********************************** -->
+
+
+</div> <!-- div_results -->
+
+</body>
+
+</html>
diff --git a/web/analysis/stats2.js b/web/analysis/stats2.js
new file mode 100644
index 0000000..651dd29
--- /dev/null
+++ b/web/analysis/stats2.js
@@ -0,0 +1,1090 @@
+// --- BEGIN Global variables -----------------------------------
+
+var database = null;
+
+// Context 1:
+var host1 = null;
+var platform1 = null;
+var branch1 = null;
+var sha11 = null;
+
+// Context 2:
+var host2 = null;
+var platform2 = null;
+var branch2 = null;
+var sha12 = null;
+
+// Plot:
+var plot = null; // Median plot.
+var overviewPlot = null; // Overview of median plot.
+var initRanges = null; // Initial (fully zoomed out) X- and Y ranges for plot.
+var currRanges = null;
+var benchmarks = null;
+var metrics = null;
+var lowerIsBetter = null;
+var coords = null;
+var logCoords = null;
+var itemSelected = null;
+var selItem_index = null;
+var sel_benchmark = null;
+var sel_metric = null;
+var sel_coordX = null;
+var sel_coordX_descr = null;
+var sel_coordY = null;
+var sel_coordY_descr = null;
+
+// --- END Global variables -------------------------------------
+
+
+function plot_clearSelection(skipRedrawOverlay) {
+ plot.clearHighlightPixel();
+ $(sel_benchmark).text("");
+ $(sel_metric).text("");
+ $(sel_coordX).text("");
+ $(sel_coordX_descr).text("");
+ $(sel_coordY).text("");
+ $(sel_coordY_descr).text("");
+
+ if (itemSelected) {
+ clearResultDetails();
+ }
+
+ itemSelected = false;
+
+ if ((skipRedrawOverlay == null) || (!skipRedrawOverlay)) {
+ plot.triggerRedrawOverlay();
+ }
+}
+
+function plot_updateHighlightPixel(index) {
+ // Update highlighting of selected point:
+ var x = logCoords[index][0];
+ var y = logCoords[index][1];
+ var xlo = currRanges.xaxis.from;
+ var xhi = currRanges.xaxis.to;
+ var ylo = currRanges.yaxis.from;
+ var yhi = currRanges.yaxis.to;
+ var xfrac = (x - xlo) / (xhi - xlo);
+ var yfrac = 1 - (y - ylo) / (yhi - ylo);
+ var px = xfrac * plot.width();
+ var py = yfrac * plot.height();
+ plot.setHighlightPixel(px, py);
+}
+
+function plot_updateSelectionDetails(index) {
+ $(sel_benchmark).text(benchmarks[index]);
+ $(sel_metric).text(metrics[index]);
+
+ var valX = null;
+ var valY = null;
+ var factImprov = null;
+ var text = null;
+ var color = null;
+
+ if (lowerIsBetter[metrics[index]]) {
+ valX = coords[index][0];
+ valY = coords[index][1];
+ factImprov = valY / valX;
+ } else {
+ valX = coords[index][1];
+ valY = coords[index][0];
+ factImprov = valX / valY;
+ }
+
+ $(sel_coordX).text(valX);
+ $(sel_coordY).text(valY);
+
+ if (factImprov > 1) {
+ text = "(" + factImprov.toFixed(4) + " X)";
+ color = betterColor;
+ } else if (factImprov < 1) {
+ text = "(" + factImprov.toFixed(4) + " X)";
+ color = worseColor;
+ } else {
+ text = "(equal)";
+ color = equalColor;
+ }
+ $(sel_coordX_descr).text(text);
+ $(sel_coordX_descr).css("color", color);
+}
+
+function plot_selectResult(index) {
+ plot_clearSelection(true);
+
+ plot_updateHighlightPixel(index);
+
+ itemSelected = true;
+ selItem_index = index;
+
+ plot_updateSelectionDetails(index);
+
+ fetchResultDetails(benchmarks[index], metrics[index]);
+
+ plot.triggerRedrawOverlay();
+}
+
+// Handles clicking a benchmark radio button.
+function table_clickBMRadioButton(cb) {
+ if (cb.checked) {
+ var index = cb.id.match(/bm_cb:(\d+)/)[1];
+ plot_selectResult(index); // synchronize plot
+ } else {
+ plot_clearSelection(); // synchronize plot
+ }
+}
+
+function table_selectRow(index) {
+ table_clearSelection();
+ $("#bm_cb\\:" + index).attr("checked", true);
+}
+
+function table_clearSelection() {
+ $("#pbmTable input").attr("checked", false);
+}
+
+// ### REFACTOR: Similar function in tsstats.js! 2 B DONE!
+function fetchResultDetails(benchmark, metric) {
+ updateStatus("fetching result details ...", true);
+
+ query = "?db=" + database +
+ "&cmd=result_details2" +
+ "&host1=" + encodeURIComponent(host1) +
+ "&platform1=" + encodeURIComponent(platform1) +
+ "&branch1=" + encodeURIComponent(branch1) +
+ "&sha11=" + sha11 +
+ "&host2=" + encodeURIComponent(host2) +
+ "&platform2=" + encodeURIComponent(platform2) +
+ "&branch2=" + encodeURIComponent(branch2) +
+ "&sha12=" + sha12 +
+ "&benchmark=" + encodeURIComponent(benchmark) +
+ "&metric=" + encodeURIComponent(metric);
+
+ url = "http://" + location.host + "/cgi-bin/getstatswrapper" + query;
+ // alert("url: >" + url + "<");
+
+ $.ajax({
+ url: url,
+ type: "GET",
+ dataType: "json",
+
+ success: function(data, textStatus, request) {
+ if (request.readyState == 4) {
+ if (request.status == 200) {
+
+ if (!itemSelected) {
+ return;
+ }
+
+ if (data.error != null) {
+ updateStatus(
+ "fetching result details ... failed: " +
+ data.error, false);
+ return
+ }
+
+ updateStatus("fetching result details ... done", false);
+ updateStatus("", false);
+
+ // Show samples ...
+ samples = { "1": data.sample1, "2": data.sample2 };
+ var currTime = dateToTimestamp(new Date());
+ for (key in samples) {
+
+ // $("#sample" + key + " option").remove();
+
+ sample = samples[key];
+ nvalid = 0;
+ for (i = 0; i < sample.length; ++i) {
+ nvalid += sample[i].valid;
+ }
+ medianIndex = Math.floor(nvalid / 2);
+ twoMedianIndexes = ((nvalid % 2) == 0);
+ validIndex = 0;
+ html = "";
+ for (i = 0; i < sample.length; ++i) {
+ value = sample[i].value;
+ valid = sample[i].valid;
+ secsAgo = currTime - sample[i].timestamp;
+ ageColor_ = ageColor(secsAgo);
+
+ if (!valid) {
+ bgColor = "#faa";
+ } else {
+ if ((validIndex == medianIndex) ||
+ (twoMedianIndexes
+ && (validIndex == (medianIndex - 1)))) {
+ bgColor = "#ff0";
+ } else {
+ bgColor = "#fff";
+ }
+ validIndex++;
+ }
+
+ // $("#sample" + key).append(
+ // "<option value=\"" + value +
+ // "\" style=\"background-color:" +
+ // bgColor + "\">" + value + "</option>");
+
+ html += "<tr>";
+ html += "<td style=\"background-color:" +
+ bgColor + "; text-align:right\">" +
+ value + "</td>";
+ html += "<td style=\"background-color:" +
+ ageColor_ + "; text-align:right\">" +
+ secsToDays(secsAgo) + "</td>";
+ html += "</tr>";
+ }
+
+ // Update table:
+ sel = "#sample" + key;
+ $(sel + " tr:gt(0)").remove();
+ $(sel + " > tbody:last").append(html);
+ $(sel).trigger("update");
+ $(sel).trigger("appendCache");
+ }
+ }
+ }
+ },
+
+ error: function(request, textStatus, errorThrown) {
+ descr = errorThrown;
+ if (errorThrown == null) {
+ descr = "undefined error - is the server down?";
+ }
+ updateStatus("fetching result details ... error: " + descr, false);
+ }
+
+ // complete: function(request, textStatus) {
+ // alert("complete; request.status: " + request.status)
+ // }
+
+ });
+}
+
+function clearResultDetails() {
+ $("#sample1 tr:gt(0)").remove();
+ $("#sample2 tr:gt(0)").remove();
+}
+
+// A Flot plugin to manipulate a highlight rectangle around a pixel.
+// ### 2 B DOCUMENTED!
+(function ($) {
+ var options = {
+ highlightPixel: {
+ color: "rgba(255, 255, 0, 1.0)",
+ radius: 2,
+ lineWidth: 2,
+ }
+ };
+
+ function init(plot) {
+
+ var pixel = { x: -1, y: -1 };
+
+ plot.setHighlightPixel = function setHighlightPixel(x, y) {
+ pixel.x = x;
+ pixel.y = y;
+ }
+
+ plot.clearHighlightPixel = function clearHighlightPixel() {
+ pixel.x = -1;
+ pixel.y = -1;
+ }
+
+ function drawHighlightPixel(plot, ctx) {
+ var x = pixel.x;
+ var y = pixel.y;
+
+ if ((x == -1) || (y == -1)) {
+ return;
+ }
+
+ var plotOffset = plot.getPlotOffset();
+ var c = plot.getOptions().highlightPixel;
+ var r = c.radius;
+
+ ctx.save();
+ ctx.translate(plotOffset.left, plotOffset.top);
+ ctx.strokeStyle = c.color;
+ ctx.lineWidth = c.lineWidth;
+
+ ctx.beginPath();
+ ctx.moveTo(x - r, y - r);
+ ctx.lineTo(x + r, y - r);
+ ctx.lineTo(x + r, y + r);
+ ctx.lineTo(x - r, y + r);
+ ctx.lineTo(x - r, y - r);
+ ctx.stroke();
+
+ ctx.restore();
+ }
+
+ plot.hooks.drawOverlay.push(drawHighlightPixel);
+ }
+
+ $.plot.plugins.push({
+ init: init,
+ options: options,
+ name: "highlightpixel",
+ version: "0.1"
+ });
+})(jQuery);
+
+
+// Creates a Flot plot that shows a set of coordinates as small dots for
+// the purpose of showing a rough overview of the performance in one
+// context versus another. The performance is expressed in terms of the
+// median value in each context.
+// ### 2 B DOCUMENTED MORE!
+function createPlot(
+ canvasId, overviewCanvasId, sizeId, benchmarkId,
+ metricId, coordXId, coordXDescrId, hoverCoordXId, hoverCoordXDescrId,
+ hoverCoordXRightParenId, coordYId, coordYDescrId, hoverCoordYId,
+ hoverCoordYDescrId, hoverCoordYRightParenId) {
+ if (coords.length == 0)
+ return;
+
+ var sel_canvas = "#" + canvasId;
+ var sel_overviewCanvas = "#" + overviewCanvasId;
+ var sel_size = "#" + sizeId;
+
+ sel_benchmark = "#" + benchmarkId;
+ sel_metric = "#" + metricId;
+
+ sel_coordX = "#" + coordXId;
+ sel_coordX_descr = "#" + coordXDescrId;
+ var sel_hoverCoordX = "#" + hoverCoordXId;
+ var sel_hoverCoordX_descr = "#" + hoverCoordXDescrId;
+ var sel_hoverCoordX_rightParen = "#" + hoverCoordXRightParenId;
+
+ sel_coordY = "#" + coordYId;
+ sel_coordY_descr = "#" + coordYDescrId;
+ var sel_hoverCoordY = "#" + hoverCoordYId;
+ var sel_hoverCoordY_descr = "#" + hoverCoordYDescrId;
+ var sel_hoverCoordY_rightParen = "#" + hoverCoordYRightParenId;
+
+ // Compute logarithmic coordinates:
+ logCoords = [];
+ var logCoords_xSmaller = []; // X coordinate smaller
+ var indexMap_xSmaller = [];
+ var logCoords_ySmaller = []; // Y coordinate smaller
+ var indexMap_ySmaller = [];
+ var logCoords_equal = []; // Less than 10% difference
+ var indexMap_equal = [];
+ var minLogValX = null;
+ var maxLogValX = null;
+ var minLogValY = null;
+ var maxLogValY = null;
+ var equalityTolerance = 0.1; // 10%
+
+ for (i = 0; i < coords.length; ++i) {
+ var x = coords[i][0];
+ var y = coords[i][1];
+ if (x <= 0) {
+ alert("Error creating plot: non-positive x coordinate found: " + x);
+ return;
+ }
+ if (y <= 0) {
+ alert("Error creating plot: non-positive y coordinate found: " + y);
+ return;
+ }
+
+ logx = Math.log(x);
+ logy = Math.log(y);
+
+ logCoords[logCoords.length] = [logx, logy];
+
+ if ((Math.abs(x - y) / Math.min(x, y)) < equalityTolerance) {
+ logCoords_equal[logCoords_equal.length] = [logx, logy];
+ indexMap_equal[indexMap_equal.length] = i;
+ } else if (x < y) {
+ logCoords_xSmaller[logCoords_xSmaller.length] = [logx, logy];
+ indexMap_xSmaller[indexMap_xSmaller.length] = i;
+ } else {
+ logCoords_ySmaller[logCoords_ySmaller.length] = [logx, logy];
+ indexMap_ySmaller[indexMap_ySmaller.length] = i;
+ }
+
+ if (i == 0) {
+ minLogValX = maxLogValX = logx;
+ minLogValY = maxLogValY = logy;
+ } else {
+ minLogValX = Math.min(minLogValX, logx);
+ maxLogValX = Math.max(maxLogValX, logx);
+ minLogValY = Math.min(minLogValY, logy);
+ maxLogValY = Math.max(maxLogValY, logy);
+ }
+ }
+
+ $(sel_size).text(
+ logCoords_xSmaller.length + logCoords_ySmaller.length
+ + logCoords_equal.length);
+
+ var minLogVal = Math.min(minLogValX, minLogValY);
+ var maxLogVal = Math.max(maxLogValX, maxLogValY);
+ var padding = 0.1 * (maxLogVal - minLogVal);
+ var minPlotVal = minLogVal - padding;
+ var maxPlotVal = maxLogVal + padding;
+ initRanges = {
+ xaxis: { from: minPlotVal, to: maxPlotVal },
+ yaxis: { from: minPlotVal, to: maxPlotVal }
+ }
+ currRanges = initRanges;
+
+ betterColor = "#0a0";
+ betterColor_pale = "#5a5";
+ worseColor = "#a00";
+ worseColor_pale = "#a55";
+ equalColor = "#555";
+
+ betterColor_point = "#0a0";
+ worseColor_point = "#f00";
+ equalColor_point = "#888";
+
+ pointRadius = 1;
+
+ var options = {
+ xaxis: {
+ min: minPlotVal,
+ max: maxPlotVal
+ },
+ yaxis: {
+ min: minPlotVal,
+ max: maxPlotVal
+ },
+ grid: {
+ show: false,
+ hoverable: true,
+ clickable: true,
+ mouseActiveRadius: 3,
+ autoHighlight: true,
+ // backgroundColor: "#ffffff"
+ backgroundColor: null // using image instead
+ },
+ highlightPixel: {
+ color: "#000",
+ radius: 3,
+ lineWidth: 2
+ },
+ lineWidth: 0,
+ selection: { mode: "xy" }
+
+ };
+
+ plot = $.plot(
+ $(sel_canvas),
+ [
+ {
+ color: betterColor_point,
+ lines: { show: false },
+ points: {
+ show: true,
+ radius: pointRadius,
+ fill: false,
+ },
+ data: logCoords_xSmaller,
+ indexMap: indexMap_xSmaller
+ },
+
+ {
+ color: worseColor_point,
+ lines: { show: false },
+ points: {
+ show: true,
+ radius: pointRadius,
+ fill: false,
+ },
+ data: logCoords_ySmaller,
+ indexMap: indexMap_ySmaller
+ },
+
+ {
+ color: equalColor_point,
+ lines: { show: false },
+ points: {
+ show: true,
+ radius: pointRadius,
+ fill: false,
+ },
+ data: logCoords_equal,
+ indexMap: indexMap_equal
+ },
+
+ // dummy points to force padding:
+ {
+ hoverable: false,
+ clickable: false,
+ color: "#aaa", // same as background color
+ shadowSize: 0,
+ lines: { show: false },
+ points: { show: true, radius: 0 },
+ data: [
+ [minPlotVal, minPlotVal]
+ ,[maxPlotVal, maxPlotVal]
+ ]
+ }
+ ],
+ options
+ );
+
+ itemSelected = false;
+ selItem_index = null;
+
+ $(sel_canvas).bind("plothover", function (event, pos, item) {
+ if (!itemSelected) {
+ if (item) {
+ plot.highlight(item.series, item.datapoint);
+ index = item.series.indexMap[item.dataIndex];
+ plot_updateSelectionDetails(index);
+ } else {
+ plot.unhighlight();
+ $(sel_benchmark).text("");
+ $(sel_metric).text("");
+ $(sel_coordX).text("");
+ $(sel_coordX_descr).text("");
+ $(sel_coordY).text("");
+ $(sel_coordY_descr).text("");
+ }
+ }
+ });
+
+ $(sel_canvas).bind("mouseleave", function (event, pos, item) {
+ $(sel_hoverCoordX).text("");
+ $(sel_hoverCoordX_descr).text("");
+ $(sel_hoverCoordX_rightParen).text("");
+ $(sel_hoverCoordY).text("");
+ $(sel_hoverCoordY_descr).text("");
+ $(sel_hoverCoordY_rightParen).text("");
+ });
+
+ $(sel_canvas).bind("plotclick", function (event, pos, item) {
+
+ plot.unhighlight();
+
+ if (item) {
+ index = item.series.indexMap[item.dataIndex];
+ plot_selectResult(index);
+ table_selectRow(index); // synchronize table
+ } else {
+ plot_clearSelection();
+ table_clearSelection(); // synchronize table
+ }
+ });
+
+
+ // Set up overview plot:
+ overviewPlot = $.plot(
+ $(sel_overviewCanvas),
+ [
+ {
+ color: betterColor_point,
+ lines: { show: false },
+ points: {
+ show: true,
+ radius: 1,
+ fill: false,
+ },
+ data: logCoords_xSmaller
+ },
+ {
+ color: worseColor_point,
+ lines: { show: false },
+ points: {
+ show: true,
+ radius: 1,
+ fill: false,
+ },
+ data: logCoords_ySmaller
+ },
+ {
+ color: equalColor_point,
+ lines: { show: false },
+ points: {
+ show: true,
+ radius: 1,
+ fill: false,
+ },
+ data: logCoords_equal
+ },
+ {
+ hoverable: false,
+ clickable: false,
+ color: "#000",
+ shadowSize: 0,
+ lines: { show: false },
+ points: { show: false },
+ data: [
+ [minPlotVal, minPlotVal]
+ ,[maxPlotVal, maxPlotVal]
+ ]
+ }
+ ],
+ {
+ series: {
+ // color: "#00ffff",
+ lines: { show: false },
+ points: { show: true, radius: 1, fill: false },
+ shadowSize: 0
+ },
+ grid: { show: false, backgroundColor: null },
+ selection: { mode: "xy" }
+ }
+ );
+
+ // Connect the main plot to the overview plot:
+ $(sel_canvas).bind("plotselected", function (event, ranges) {
+
+ currRanges = ranges;
+
+ // Clamp the zooming to prevent eternal zoom:
+ if (ranges.xaxis.to - ranges.xaxis.from < 0.00001)
+ ranges.xaxis.to = ranges.xaxis.from + 0.00001;
+ if (ranges.yaxis.to - ranges.yaxis.from < 0.00001)
+ ranges.yaxis.to = ranges.yaxis.from + 0.00001;
+
+ // Do the zooming:
+ plot = $.plot(
+ $(sel_canvas),
+ [
+ {
+ color: betterColor_point,
+ lines: { show: false },
+ points: {
+ show: true,
+ radius: pointRadius,
+ fill: false,
+ },
+ data: logCoords_xSmaller,
+ indexMap: indexMap_xSmaller
+ },
+ {
+ color: worseColor_point,
+ lines: { show: false },
+ points: {
+ show: true,
+ radius: pointRadius,
+ fill: false,
+ },
+ data: logCoords_ySmaller,
+ indexMap: indexMap_ySmaller
+ },
+ {
+ color: equalColor_point,
+ lines: { show: false },
+ points: {
+ show: true,
+ radius: pointRadius,
+ fill: false,
+ },
+ data: logCoords_equal,
+ indexMap: indexMap_equal
+ }
+ // ,
+ // {
+ // hoverable: false,
+ // clickable: false,
+ // color: "#ff0000",
+ // shadowSize: 0,
+ // lines: { show: true },
+ // points: { show: false },
+ // data: [
+ // [minPlotVal, minPlotVal]
+ // ,[maxPlotVal, maxPlotVal]
+ // ]
+ // }
+ ],
+
+ $.extend(true, {}, options, {
+ xaxis: { min: ranges.xaxis.from, max: ranges.xaxis.to },
+ yaxis: { min: ranges.yaxis.from, max: ranges.yaxis.to }
+ }));
+
+ if (itemSelected) {
+ plot_updateHighlightPixel(selItem_index);
+ plot.triggerRedrawOverlay();
+ }
+
+ // Don't fire event on the overview to prevent eternal loop:
+ overviewPlot.setSelection(ranges, true);
+ });
+ $(sel_overviewCanvas).bind("plotselected", function (event, ranges) {
+ plot.setSelection(ranges);
+ });
+}
+
+function fetchStats(testCaseFilter) {
+
+ updateStatus("fetching statistics ...", true);
+
+ query = "?db=" + database +
+ "&cmd=stats2" +
+ "&host1=" + encodeURIComponent(host1) +
+ "&platform1=" + encodeURIComponent(platform1) +
+ "&branch1=" + encodeURIComponent(branch1) +
+ "&sha11=" + sha11 +
+ "&host2=" + encodeURIComponent(host2) +
+ "&platform2=" + encodeURIComponent(platform2) +
+ "&branch2=" + encodeURIComponent(branch2) +
+ "&sha12=" + sha12;
+
+ if (testCaseFilter != "") {
+ query += "&testcasefilter=" + testCaseFilter;
+ }
+
+ url = "http://" + location.host + "/cgi-bin/getstatswrapper" + query;
+ //alert("url: >" + url + "<");
+
+ function setImprovText(sel, val, suffix) {
+ fact = Math.pow(2, val);
+ betterColor = "#0a0";
+ worseColor = "#a00";
+ equalColor = "#555";
+ sfx = ((suffix != null) && suffix);
+ if (fact > 1) {
+ $(sel).text(fact.toFixed(4) + " X" + (sfx ? " better" : ""));
+ $(sel).css("color", betterColor);
+ $(sel).css("text-align", "right");
+ } else if (fact < 1) {
+ $(sel).text(fact.toFixed(4) + " X" + (sfx ? " better" : ""));
+ $(sel).css("color", worseColor);
+ $(sel).css("text-align", "right");
+ } else {
+ $(sel).text("equal");
+ $(sel).css("color", equalColor);
+ }
+ }
+
+ function getImprovFactColorAndText(log2improvFact) {
+ color = "#f00";
+ text = "n/a";
+ if (log2improvFact != "") {
+ improvFact = Math.pow(2, log2improvFact);
+ if (improvFact > 1.1) {
+ color = "#0a0";
+ } else if (improvFact < (1 / 1.1)) {
+ color = "#a00";
+ } else {
+ color = "#000";
+ }
+ text = improvFact.toFixed(4) + " X";
+ }
+ return {"color": color, "text": text};
+ }
+
+ // Assume -1 <= improvDiff <= 1
+ function getImprovDiffColorAndText(improvDiff) {
+ color = "#f00";
+ text = "n/a";
+ if (improvDiff != "") {
+ improvDiff = parseFloat(improvDiff);
+ if (improvDiff > 0.1) {
+ color = "#0a0";
+ } else if (improvDiff < -0.1) {
+ color = "#a00";
+ } else {
+ color = "#000";
+ }
+ text = improvDiff.toFixed(4);
+ }
+ return {"color": color, "text": text};
+ }
+
+
+ $.ajax({
+ url: url,
+ type: "GET",
+ dataType: "json",
+
+ success: function(data, textStatus, request) {
+ if (request.readyState == 4) {
+ if (request.status == 200) {
+
+ if (data.error != null) {
+ updateStatus(
+ "fetching statistics ... failed: " +
+ data.error, false);
+ return
+ }
+
+ updateStatus("fetching statistics ... done", false);
+ updateStatus("", false);
+
+ // Show context ...
+ $("#shown_database").text(data.database);
+ $("#shown_host1").text(data.host1);
+ $("#shown_platform1").text(data.platform1);
+ $("#shown_branch1").text(data.branch1);
+ $("#shown_sha11").text(data.sha11);
+ $("#shown_host2").text(data.host2);
+ $("#shown_platform2").text(data.platform2);
+ $("#shown_branch2").text(data.branch2);
+ $("#shown_sha12").text(data.sha12);
+
+
+ // --- BEGIN Populate overall stats table ---
+ $("#nbenchmarks").text(data.nbenchmarks);
+ $("#ntestcases").text(data.ntestcases);
+ $("#nmedian_improvs").text(data.nmedian_improvs);
+
+ setImprovText(
+ "#mean_of_median_improvs", data.mean_of_median_improvs);
+ setImprovText(
+ "#context2_descr", data.mean_of_median_improvs, true);
+ setTooltip(
+ $("#mean_of_median_improvs"),
+ "Arithmetic mean of the median improvements " +
+ "from Context 1 to Context 2.");
+
+ percValues = {
+ "10": data.perc_10_of_median_improvs,
+ "25": data.perc_25_of_median_improvs,
+ "50": data.perc_50_of_median_improvs,
+ "75": data.perc_75_of_median_improvs,
+ "90": data.perc_90_of_median_improvs
+ };
+ for (key in percValues) {
+ setImprovText(
+ "#perc_" + key + "_of_median_improvs",
+ percValues[key]);
+ setTooltip(
+ $("#perc_" + key + "_of_median_improvs"),
+ "The value below which " + key + "% of the " +
+ "median improvements from Context 1 " +
+ "to Context 2 fall.<br /><br />" +
+ "Context 2 improves by this factor or less " +
+ "for the worst " + key + "% of the " +
+ "benchmarks (and improves by this factor " +
+ "or more for the best " + (100 - key) +
+ "%).");
+ }
+
+ $("#better_than_fact_1_1_count").text(
+ data.better_than_fact_1_1_count);
+
+ $("#equal_count").text(
+ parseInt(data.nmedian_improvs) -
+ (parseInt(data.better_than_fact_1_1_count) +
+ parseInt(data.worse_than_fact_inv_1_1_count)));
+ $("#worse_than_fact_inv_1_1_count").text(
+ data.worse_than_fact_inv_1_1_count);
+
+ // --- END Populate overall stats table ---
+
+
+ // --- BEGIN Populate per-benchmark stats table ---
+
+ // Remove all rows below the header ...
+ $("#pbmTable tr:gt(0)").remove();
+
+ pbm_stats = data.per_bm_stats;
+
+ // Show # of per-benchmark rows ...
+ $("#pbmTable_nrows").text(pbm_stats.length);
+
+ html = "";
+ coords = [];
+ benchmarks = [];
+ metrics = [];
+ lowerIsBetter = new Object();
+ for (var i = 0; i < pbm_stats.length; ++i) {
+ row = pbm_stats[i];
+
+ median1 = row[0];
+ median2 = row[1];
+ medianImprov = row[2];
+ rse1 = row[3];
+ rse2 = row[4];
+ rseImprov = row[5];
+ metric = row[6];
+ lib = parseInt(row[7]);
+ benchmark = row[8];
+
+ // Append to plottable array ...
+ x = parseFloat(median2);
+ y = parseFloat(median1);
+ if (!(isNaN(x) || isNaN(y) || (x <= 0) || (y <= 0))) {
+ index = coords.length;
+ if (lib) {
+ coords[index] = [x, y];
+ } else {
+ coords[index] = [y, x];
+ }
+ benchmarks[index] = benchmark;
+ metrics[index] = metric;
+ lowerIsBetter[metric] = lib;
+
+ firstColHtml = "<input id=\"bm_cb:" + index +
+ "\" name=\"currSelector\" " +
+ "type=\"radio\" onclick=\"" +
+ "table_clickBMRadioButton(this)\">";
+ } else {
+ firstColHtml =
+ "<span style=\"color:red\">n/a</span>";
+ }
+
+ // Append to HTML ...
+ html += "<tr>";
+
+ html += "<td>" + firstColHtml + "</td>"
+
+ html += "<td class=\"context1\">" + median1 + "</td>";
+ html += "<td class=\"context2\">" + median2 + "</td>";
+ ict = getImprovFactColorAndText(medianImprov);
+ html += "<td style=\"color: " + ict.color +
+ "; text-align:right\">" + ict.text + "</td>";
+
+ html += "<td class=\"context1\" " +
+ "style=\"text-align:right\">" +
+ ((rse1 != "") ? parseFloat(rse1).toFixed(2) : "") +
+ "</td>";
+ html += "<td class=\"context2\" " +
+ "style=\"text-align:right\">" +
+ ((rse2 != "") ? parseFloat(rse2).toFixed(2) : "") +
+ "</td>";
+ ict = getImprovDiffColorAndText(rseImprov);
+ html += "<td style=\"color: " + ict.color +
+ "; text-align:right\">" + ict.text + "</td>";
+
+ html += "<td class=\"metric\">" + metric + "</td>";
+ html += "<td class=\"benchmark\">" + benchmark +
+ "</td>";
+ html += "</tr>";
+ }
+
+ $("#pbmTable > tbody:last").append(html);
+ $("#pbmTable").trigger("update");
+ $("#pbmTable").trigger("appendCache");
+
+ // var sorting = [[11,1],[0,0]];
+ //$("table").trigger("sorton",[sorting]);
+
+ // --- END Populate per-benchmark stats table ---
+
+ $("#div_results").css("display", "block");
+
+ createPlot(
+ "plot_canvas_medians", "plot_overview_canvas_medians",
+ "plot_size", "plot_benchmark", "plot_metric",
+ "plot_median2", "plot_median2_descr",
+ "plot_hoverMedian2", "plot_hoverMedian2_descr",
+ "plot_hoverMedian2_rightParen",
+ "plot_median1", "plot_median1_descr",
+ "plot_hoverMedian1", "plot_hoverMedian1_descr",
+ "plot_hoverMedian1_rightParen");
+ }
+ }
+ },
+
+ error: function(request, textStatus, errorThrown) {
+ descr = errorThrown;
+ if (errorThrown == null) {
+ descr = "undefined error - is the server down?";
+ }
+ updateStatus("fetching statistics ... error: " + descr, false);
+ }
+
+ // complete: function(request, textStatus) {
+ // alert("complete; request.status: " + request.status)
+ // }
+
+ });
+}
+
+$(document).ready(function() {
+
+ $.tablesorter.addParser({
+ id: "mixed_numeric",
+ is: function(s) {
+ return false; // so this parser is not auto detected
+ },
+ format: function(s) {
+ f = parseFloat(s);
+ if (isNaN(f)) { return ""; }
+ return f.toFixed(20); // max precision guaranteed by ECMA standard
+ },
+ type: "numeric"
+ });
+
+ $("#pbmTable").tablesorter({
+ headers: {
+ 0: { sorter: false },
+ // handle proper sorting of mixed standard- and scientific notation:
+ 1: { sorter: "mixed_numeric" }, // median 1
+ 2: { sorter: "mixed_numeric" }, // median 2
+ 3: { sorter: "mixed_numeric" }, // median improvement factor
+ 4: { sorter: "mixed_numeric" }, // relative standard error 1
+ 5: { sorter: "mixed_numeric" }, // relative standard error 2
+ 6: { sorter: "mixed_numeric" } // RSE improvement factor
+ }
+ });
+
+ $("#pbmTable").bind("sortStart",function() {
+ $("#pbmTable_sortInProgress").show();
+ }).bind("sortEnd",function() {
+ $("#pbmTable_sortInProgress").hide();
+ });
+
+ $("#sample1").tablesorter({
+ headers: {
+ 0: { sorter: "mixed_numeric" },
+ 1: { sorter: "mixed_numeric" }
+ }
+ });
+
+ $("#sample2").tablesorter({
+ headers: {
+ 0: { sorter: "mixed_numeric" },
+ 1: { sorter: "mixed_numeric" }
+ }
+ });
+
+ args = queryStringArgs();
+
+ // Extract database (required):
+ database = extractArg(args, "db");
+ if (database == "") {
+ alert("ERROR: invalid query string (empty database)");
+ return;
+ }
+
+ // Extract context 1 (required):
+ host1 = extractArg(args, "host1");
+ if (host1 == "") {
+ alert("ERROR: invalid query string (empty host)");
+ return;
+ }
+ platform1 = extractArg(args, "platform1");
+ if (platform1 == "") {
+ alert("ERROR: invalid query string (empty platform)");
+ return;
+ }
+ branch1 = extractArg(args, "branch1");
+ if (branch1 == "") {
+ alert("ERROR: invalid query string (empty branch)");
+ return;
+ }
+ sha11 = extractArg(args, "sha11");
+ if (sha11 == "") {
+ alert("ERROR: invalid query string (empty sha1)");
+ return;
+ }
+
+ // Extract context 2 (optional):
+ host2 = extractArg(args, "host2");
+ platform2 = extractArg(args, "platform2");
+ branch2 = extractArg(args, "branch2");
+ sha12 = extractArg(args, "sha12");
+
+ // Extract test case filter (optional);
+ var testCaseFilter = extractArg(args, "testcasefilter");
+
+ $("#div_results").css("display", "none");
+
+ fetchStats(testCaseFilter);
+});
+
+function zoomOutMedianPlot() {
+ plot.setSelection(initRanges);
+ overviewPlot.clearSelection(true);
+ return false;
+}
diff --git a/web/analysis/style.css b/web/analysis/style.css
new file mode 100644
index 0000000..c1bc2d5
--- /dev/null
+++ b/web/analysis/style.css
@@ -0,0 +1,116 @@
+body {
+ font-family: normal small verdana, arial, helvetica, sans-serif;
+ font-size: 12;
+ margin: 5px;
+ background-color: white;
+}
+
+a.actionButton:link, a.actionButton:visited {
+ display:inline;
+ /* font-weight:bold; */
+ font-size:14px;
+ color:#000;
+ background-color:#eee8ce;
+ border-style:solid;
+ border-color:#fff9dc #ddd8bf #ddd8bf #fff9dc;
+ text-align:center;
+ /* padding:4px; */
+ /* margin-top:5px; */
+ /* margin-bottom:5px; */
+ text-decoration:none;
+}
+
+a.actionButton:hover, a.actionButton:active {
+ background-color:#e6e1c7;
+}
+
+a.disabledActionButton:link,
+a.disabledActionButton:visited,
+a.disabledActionButton:hover,
+a.disabledActionButton:active {
+ display:inline;
+ font-size:14px;
+ color:#999;
+ background-color:#eee;
+ border-style:solid;
+ border-color:#fdfdfd #dfdfdf #dfdfdf #fdfdfd;
+ text-align:center;
+ text-decoration:none;
+}
+
+table {
+ border-collapse:collapse;
+ font-size:12px;
+}
+
+table, th, td {
+ border: 1px solid #aaaaaa;
+ padding:3;
+}
+
+/* Note: In the below classes, an image is used instead of an explicit
+ background color to work around a limitation in the
+ jQuery tablesorter plugin. */
+.context1 {
+ /* background-color:...; */
+ background-image:url("images/bg_context1.png");
+}
+.context2 {
+ /* background-color:...; */
+ background-image:url("images/bg_context2.png");
+}
+.metric {
+ /* background-color:...; */
+ background-image:url("images/bg_metric.png");
+}
+.benchmark {
+ /* background-color:...; */
+ background-image:url("images/bg_benchmark.png");
+}
+
+.unselected {
+ background-color:#ffffff;
+}
+
+.tooltipHeader1 {
+ background-color:#dd0;
+/* width:400px; */
+ opacity: 0.95;
+}
+.tooltipBody1 {
+ background-color:#ffb;
+/* width:400px; */
+/* opacity: 0.95; */
+}
+
+.leadText {
+ font-weight:bold;
+ font-size:11px;
+ text-align:right;
+ background-color:#eee;
+}
+.leadText_thin {
+ font-weight:regular;
+ font-size:11px;
+ text-align:right;
+ background-color:#eee;
+}
+.contentText_pad {
+ font-weight:regular;
+ font-size:11px;
+ text-align:left;
+ padding-right:20px;
+}
+.contentText_mono {
+ font-family: "Lucida Console", "Courier New", monospace;
+ font-weight:regular;
+ font-size:11px;
+ text-align:left;
+}
+.contentText_mono_pad {
+ font-family: "Lucida Console", "Courier New", monospace;
+ font-weight:regular;
+ font-size:11px;
+ text-align:left;
+ padding-right:20px;
+}
diff --git a/web/analysis/tablesorter/themes/bm/asc.png b/web/analysis/tablesorter/themes/bm/asc.png
new file mode 100644
index 0000000..1ebe2cf
--- /dev/null
+++ b/web/analysis/tablesorter/themes/bm/asc.png
Binary files differ
diff --git a/web/analysis/tablesorter/themes/bm/bg.png b/web/analysis/tablesorter/themes/bm/bg.png
new file mode 100644
index 0000000..b7be99f
--- /dev/null
+++ b/web/analysis/tablesorter/themes/bm/bg.png
Binary files differ
diff --git a/web/analysis/tablesorter/themes/bm/desc.png b/web/analysis/tablesorter/themes/bm/desc.png
new file mode 100644
index 0000000..5f445f8
--- /dev/null
+++ b/web/analysis/tablesorter/themes/bm/desc.png
Binary files differ
diff --git a/web/analysis/tablesorter/themes/bm/style.css b/web/analysis/tablesorter/themes/bm/style.css
new file mode 100644
index 0000000..72b9549
--- /dev/null
+++ b/web/analysis/tablesorter/themes/bm/style.css
@@ -0,0 +1,43 @@
+table.tablesorter {
+ font-size: 12px;
+ /* background-color: #4D4D4D; */
+ background-color: #fff;
+ width: 1024px;
+ border: 1px solid #000;
+}
+table.tablesorter th {
+ text-align: left;
+ padding: 5px;
+ /* background-color: #6E6E6E; */
+ background-color: #fff;
+}
+table.tablesorter td {
+ /* color: #FFF; */
+ color: #000;
+ padding: 5px;
+}
+table.tablesorter .even {
+ background-color: #fff;
+}
+table.tablesorter .odd {
+ background-color: #6E6E6E;
+}
+table.tablesorter .header {
+ background-image: url(bg.png);
+ background-repeat: no-repeat;
+ border-left: 1px solid #FFF;
+ border-right: 1px solid #000;
+ border-top: 1px solid #FFF;
+ /* padding-left: 30px; */
+ padding-left: 20px;
+ padding-top: 8px;
+ height: auto;
+}
+table.tablesorter .headerSortUp {
+ background-image: url(asc.png);
+ background-repeat: no-repeat;
+}
+table.tablesorter .headerSortDown {
+ background-image: url(desc.png);
+ background-repeat: no-repeat;
+}
diff --git a/web/analysis/tsbm.js b/web/analysis/tsbm.js
new file mode 100644
index 0000000..722c36d
--- /dev/null
+++ b/web/analysis/tsbm.js
@@ -0,0 +1,146 @@
+function fetchSnapshots(
+ database, host, platform, branch, sha11, sha12, benchmark, metric, difftol,
+ durtolmin, durtolmax) {
+ updateStatus("fetching snapshots ...", true);
+
+ query = "?db=" + database +
+ "&cmd=snapshots" +
+ "&host=" + encodeURIComponent(host) +
+ "&platform=" + encodeURIComponent(platform) +
+ "&branch=" + encodeURIComponent(branch) +
+ "&sha11=" + sha11 +
+ "&sha12=" + sha12;
+
+ url = "http://" + location.host + "/cgi-bin/getstatswrapper" + query;
+ //alert("url: >" + url + "<");
+
+ $.ajax({
+ url: url,
+ type: "GET",
+ dataType: "json",
+
+ success: function(data, textStatus, request) {
+ if (request.readyState == 4) {
+ if (request.status == 200) {
+
+ if (data.error != null) {
+ updateStatus(
+ "fetching snapshots ... failed: " +
+ data.error, false);
+ return;
+ }
+
+ updateStatus("fetching snapshots ... done", false);
+ updateStatus("", false);
+
+ setSnapshots(data.snapshots);
+
+ $("#div_tsbm").css("display", "block");
+
+ // Fetch and plot time series:
+ fetchTimeSeries(
+ database, host, platform, branch, sha11, sha12,
+ benchmark, metric, difftol, durtolmin, durtolmax,
+ false);
+ }
+ }
+ },
+
+ error: function(request, textStatus, errorThrown) {
+ descr = errorThrown;
+ if (errorThrown == null) {
+ descr = "undefined error - is the server down?";
+ }
+ updateStatus("fetching snapshots ... error: " + descr, false);
+ }
+
+ // complete: function(request, textStatus) {
+ // alert("complete; request.status: " + request.status)
+ // }
+
+ });
+}
+
+$(document).ready(function() {
+
+ initTablesorter();
+ initTSBMBody();
+
+ var args = queryStringArgs();
+
+ database = extractArg(args, "db");
+ if (database == "") {
+ alert("ERROR: invalid query string (empty database)");
+ return;
+ }
+ host = extractArg(args, "host");
+ if (host == "") {
+ alert("ERROR: invalid query string (empty host)");
+ return;
+ }
+ platform = extractArg(args, "platform");
+ if (platform == "") {
+ alert("ERROR: invalid query string (empty platform)");
+ return;
+ }
+ branch = extractArg(args, "branch");
+ if (branch == "") {
+ alert("ERROR: invalid query string (empty branch)");
+ return;
+ }
+ var sha11 = extractArg(args, "sha11");
+ if (sha11 == "") {
+ alert("ERROR: invalid query string (empty sha11)");
+ return;
+ }
+ var sha12 = extractArg(args, "sha12");
+ if (sha11 == "") {
+ alert("ERROR: invalid query string (empty sha12)");
+ return;
+ }
+ var benchmark = extractArg(args, "benchmark");
+ if (benchmark == "") {
+ alert("ERROR: invalid query string (empty benchmark)");
+ return;
+ }
+ var metric = extractArg(args, "metric");
+ if (metric == "") {
+ alert("ERROR: invalid query string (empty metric)");
+ return;
+ }
+ var difftol = extractArg(args, "difftol");
+ if (difftol == "") {
+ alert("ERROR: invalid query string (empty difftol)");
+ return;
+ }
+ var durtolmin = extractArg(args, "durtolmin");
+ if (durtolmin == "") {
+ alert("ERROR: invalid query string (empty durtolmin)");
+ return;
+ }
+ var durtolmax = extractArg(args, "durtolmax");
+ if (durtolmax == "") {
+ alert("ERROR: invalid query string (empty durtolmax)");
+ return;
+ }
+
+ $("#div_tsbm_border").css("display", "none");
+ $("#div_tsbm").css("display", "none");
+ $("#div_perBenchmarkStats").css("display", "none");
+
+ // Show context ...
+ $("#main_context_database").text(database);
+ $("#main_context_host").text(host);
+ $("#main_context_platform").text(platform);
+ $("#main_context_branch").text(branch);
+ $("#main_context_sha11").text(sha11);
+ $("#main_context_sha12").text(sha12);
+ $("#main_context_difftol").text(difftol);
+ $("#main_context_durtolmin").text(durtolmin);
+ $("#main_context_durtolmax").text(durtolmax);
+
+ // Fetch snapshots:
+ fetchSnapshots(
+ database, host, platform, branch, sha11, sha12, benchmark, metric,
+ difftol, durtolmin, durtolmax);
+});
diff --git a/web/analysis/tsbm.shtml b/web/analysis/tsbm.shtml
new file mode 100644
index 0000000..54141ce
--- /dev/null
+++ b/web/analysis/tsbm.shtml
@@ -0,0 +1,53 @@
+<html>
+
+<head>
+
+ <title>BM2 - Benchmark Time Series</title>
+
+ <script type="text/javascript" src="jquery-1.4.2.min.js"></script>
+ <script type="text/javascript" src="flot/jquery.flot.js"></script>
+ <script type="text/javascript" src="flot/jquery.flot.selection.js"></script>
+ <script type="text/javascript" src="tablesorter/jquery.tablesorter.js">
+ </script>
+ <script type="text/javascript" src="boxover/boxover.js"></script>
+ <script type="text/javascript" src="global.js"></script>
+ <script type="text/javascript" src="tsbmbody.js"></script>
+ <script type="text/javascript" src="tsbm.js"></script>
+
+ <link type="text/css" rel="stylesheet" href="style.css" />
+ <link type="text/css" rel="stylesheet" href="tablesorter/docs/css/jq.css" />
+ <link type="text/css" rel="stylesheet"
+ href="tablesorter/themes/bm/style.css" />
+
+</head>
+
+<body>
+
+<span id="title" style="font-size:18; font-weight:bold">
+BM2 - Benchmark Time Series</span>
+&nbsp;&nbsp;&nbsp;
+<span style="white-space:nowrap">
+<span id="status3">no status</span>
+<img alt="spinner3" id="spinner3" src="images/ajax-spinner.gif"
+ style="display:none"/>
+<img alt="nospinner3" id="nospinner3" src="images/nospinner.png"
+ style="display:inline"/>
+</span>
+
+<!--
+<br />
+<a id="mainPageLink" href="">main page</a>
+<script type="text/javascript">
+$("#mainPageLink").attr("href", "http://" + location.host + "/bm2");
+</script>
+<br />
+<br />
+-->
+
+<br /><br />
+
+<!--#include file="tsbmbody.html" -->
+
+</body>
+
+</html>
diff --git a/web/analysis/tsbmbody.html b/web/analysis/tsbmbody.html
new file mode 100644
index 0000000..69fed8d
--- /dev/null
+++ b/web/analysis/tsbmbody.html
@@ -0,0 +1,469 @@
+<div id="div_tsbm" style="display:none">
+
+<!-- *** BEGIN Main context and selected time series stats ************* -->
+<table style="border:0">
+<tr>
+ <td style="border:0">
+ <a href="help_tsbm.html">help</a>
+ </td>
+</tr>
+
+<tr>
+
+<!-- *** BEGIN Main context *********************************** -->
+<td style="border:0" valign=top>
+<span style="font-size:14; font-weight:bold">Main context:</span>
+<table>
+ <tr>
+ <td class="leadText" id="main_context_database_lead"><b>Database:</b></td>
+ <td id="main_context_database"></td>
+ <script type="text/javascript">
+ setTooltip(
+ $("#main_context_database_lead"),
+ "The database from which the results were extracted.")
+ </script>
+ <td class="leadText" id="main_context_date_lead"><b>Report date:</b></td>
+ <td id="main_context_date"></td>
+ <script type="text/javascript">
+ setTooltip(
+ $("#main_context_date_lead"),
+ "The date at which this web page was generated.")
+ </script>
+ </tr>
+ <tr>
+ <td class="leadText" id="main_context_host_lead"><b>Host:</b></td>
+ <td id="main_context_host" colspan=3></td>
+ <script type="text/javascript">
+ setTooltip(
+ $("#main_context_host_lead"),
+ "The physical computer on which the benchmark producing the " +
+ "results was executed. <b>Note:</b>It is assumed that the " +
+ "HW/SW specifications of the host does not change " +
+ "significantly during the time span of the time series. " +
+ "(The principle here being that significant changes in the " +
+ "time series should be caused by changes in the product " +
+ "(i.e. Qt) only!)")
+ </script>
+ </tr>
+ <tr>
+ <td class="leadText" id="main_context_platform_lead"><b>Platform:</b></td>
+ <td id="main_context_platform" colspan=3></td>
+ <script type="text/javascript">
+ setTooltip(
+ $("#main_context_platform_lead"),
+ "The general environment used for building and executing the " +
+ "product being measured (typically an OS/compiler combination).")
+ </script>
+ </tr>
+ <tr>
+ <td class="leadText" id="main_context_branch_lead"><b>Branch:</b></td>
+ <td id="main_context_branch" colspan=3></td>
+ <script type="text/javascript">
+ setTooltip(
+ $("#main_context_branch_lead"),
+ "Essentially the version of the product being measured. " +
+ "The branch is normally made up of two components: " +
+ "The <i>git repository</i> and the <i>git branch</i>.")
+ </script>
+ </tr>
+</table>
+
+<table>
+ <tr>
+ <td style="text-align:left" colspan=3>
+ <span class="leadText" id="main_context_snapshots_lead">
+ Target snapshots (</span><span
+ class="leadText_thin" id="main_context_nsnapshots"></span><span
+ class="leadText">):</span>
+ </td>
+ <script type="text/javascript">
+ setTooltip(
+ $("#main_context_snapshots_lead"),
+ "The requested subsequence of snapshots for which relevant " +
+ "results potentially exist in the database. The number of " +
+ "target snapshots is shown in parentheses.")
+ </script>
+ <td class="leadText" id="main_context_days_ago"><b>Days ago:</b></td>
+ <script type="text/javascript">
+ setTooltip(
+ $("#main_context_days_ago"),
+ "Days ago (relative to the report date) since the first " +
+ "observation for this snapshot was uploaded to the database.")
+ </script>
+ </tr>
+ <tr>
+ <td></td><td class="leadText"><b>From SHA-1:</b></td>
+ <td class="contentText_mono_pad" id="main_context_sha11"></td>
+ <td id="main_context_sha11_daysago" style="text-align:right"></td>
+ </tr>
+ <tr>
+ <td></td><td class="leadText"><b>To SHA-1:</b></td>
+ <td class="contentText_mono_pad" id="main_context_sha12"></td>
+ <td id="main_context_sha12_daysago" style="text-align:right"></td>
+ </tr>
+ <tr>
+ <td class="leadText" id="main_context_difftol_lead">
+ <b>Difference tolerance:</b>
+ </td>
+ <td id="main_context_difftol" colspan=3></td>
+ <script type="text/javascript">
+ setTooltip(
+ $("#main_context_difftol_lead"),
+ "A real value &ge; 1 that decides whether a change between two " +
+ "median observations is considered significant or not.")
+ </script>
+ </tr>
+ <tr>
+ <td class="leadText" id="main_context_durtolmin_lead">
+ <b>Minimum durability tolerance:</b>
+ </td>
+ <td id="main_context_durtolmin" colspan=3></td>
+ <script type="text/javascript">
+ setTooltip(
+ $("#main_context_durtolmin_lead"),
+ "The minimum length a contiguous sequence of significantly " +
+ "equal median observations must have for it to achieve a " +
+ "durability score greater than zero. Once the sequence is at " +
+ "least this long, the durability score grows linearly to 1 at " +
+ "a rate that depends on the maximum durability tolerance.")
+ </script>
+ </tr>
+ <tr>
+ <td class="leadText" id="main_context_durtolmax_lead">
+ <b>Maximum durability tolerance:</b>
+ </td>
+ <td id="main_context_durtolmax" colspan=3></td>
+ <script type="text/javascript">
+ setTooltip(
+ $("#main_context_durtolmax_lead"),
+ "The length of a contiguous sequence of significantly equal median " +
+ "observations that is sufficient to achieve the maximum " +
+ "durability score of 1. The durability score for shorter " +
+ "sequences falls linearly to 0 at a rate that depends on the " +
+ "minimum durability tolerance.")
+ </script>
+ </tr>
+</table>
+</td>
+<!-- *** END Main context *********************************** -->
+
+<!-- *** BEGIN Selected time series stats ******************************* -->
+<td valign=top style="border:0; padding-left:20">
+<span id="tgt_snapshots" style="font-size:14; font-weight:bold">
+ Time series statistics:</span>
+<table>
+ <tr>
+ <td class="leadText" id="lead_ms"><b>MS:</b></td>
+ <td class="contentText_pad" id="bmstats_ms"></td>
+ <script type="text/javascript">
+ setTooltip($("#lead_ms"), tooltipText_ms())</script>
+
+ <td class="leadText" id="lead_lsd"><b>LSD:</b></td>
+ <td class="contentText_pad" id="bmstats_lsd" colspan=3></td>
+ <script type="text/javascript">
+ setTooltip($("#lead_lsd"), tooltipText_lsd())</script>
+ </tr>
+ <tr>
+ <td class="leadText" id="lead_ni"><b>NI:</b></td>
+ <td class="contentText_pad" id="bmstats_ni"></td>
+ <script type="text/javascript">
+ setTooltip($("#lead_ni"), tooltipText_ni())</script>
+
+ <td class="leadText" id="lead_nz"><b>NZ:</b></td>
+ <td class="contentText_pad" id="bmstats_nz"></td>
+ <script type="text/javascript">
+ setTooltip($("#lead_nz"), tooltipText_nz())</script>
+
+ <td class="leadText" id="lead_nc"><b>NC:</b></td>
+ <td class="contentText_pad" id="bmstats_nc"></td>
+ <script type="text/javascript">
+ setTooltip($("#lead_nc"), tooltipText_nc())</script>
+ </tr>
+ <tr>
+ <td class="leadText" id="lead_mdrse"><b>MDRSE:</b></td>
+ <td class="contentText_pad" id="bmstats_mdrse"></td>
+ <script type="text/javascript">
+ setTooltip($("#lead_mdrse"), tooltipText_mdrse())</script>
+
+ <td class="leadText" id="lead_rsemd"><b>RSEMD:</b></td>
+ <td class="contentText_pad" id="bmstats_rsemd" colspan=3></td>
+ <script type="text/javascript">
+ setTooltip($("#lead_rsemd"), tooltipText_rsemd())</script>
+ </tr>
+ <tr>
+ <td class="leadText" id="lead_qs"><b>QS:</b></td>
+ <td class="contentText_pad" id="bmstats_qs" colspan=5></td>
+ <script type="text/javascript">
+ setTooltip($("#lead_qs"), tooltipText_qs())</script>
+ </tr>
+ <tr>
+ <td class="leadText" id="lead_lc"><b>LC:</b></td>
+ <td class="contentText_pad" id="bmstats_lc"></td>
+ <script type="text/javascript">
+ setTooltip($("#lead_lc"), tooltipText_lc())</script>
+
+ <td class="leadText" id="lead_lcda"><b>LCDA:</b></td>
+ <td class="contentText_pad" id="bmstats_lcda" colspan=3></td>
+ <script type="text/javascript">
+ setTooltip($("#lead_lcda"), tooltipText_lcda())</script>
+ <tr>
+ <td class="leadText" id="lead_lcms"><b>LCMS:</b></td>
+ <td class="contentText_pad" id="bmstats_lcms" colspan=5></td>
+ <script type="text/javascript">
+ setTooltip($("#lead_lcms"), tooltipText_lcms())</script>
+ </tr>
+ <tr>
+ <td class="leadText" id="lead_lcss"><b>LCSS:</b></td>
+ <td class="contentText_pad" id="bmstats_lcss"></td>
+ <script type="text/javascript">
+ setTooltip($("#lead_lcss"), tooltipText_lcss())</script>
+
+ <td class="leadText" id="lead_lcss1"><b>LCSS1:</b></td>
+ <td class="contentText_pad" id="bmstats_lcss1" colspan=3></td>
+ <script type="text/javascript">
+ setTooltip($("#lead_lcss1"), tooltipText_lcss1())</script>
+ </tr>
+ <tr>
+ <td class="leadText" id="lead_lcgss"><b>LCGSS:</b></td>
+ <td class="contentText_pad" id="bmstats_lcgss"></td>
+ <script type="text/javascript">
+ setTooltip($("#lead_lcgss"), tooltipText_lcgss())</script>
+
+ <td class="leadText" id="lead_lclss"><b>LCLSS:</b></td>
+ <td class="contentText_pad" id="bmstats_lclss" colspan=3></td>
+ <script type="text/javascript">
+ setTooltip($("#lead_lclss"), tooltipText_lclss())</script>
+ </tr>
+ <tr>
+ <td class="leadText" id="lead_lcds1"><b>LCDS1:</b></td>
+ <td class="contentText_pad" id="bmstats_lcds1"></td>
+ <script type="text/javascript">
+ setTooltip($("#lead_lcds1"), tooltipText_lcds1())</script>
+
+ <td class="leadText" id="lead_lcds2"><b>LCDS2:</b></td>
+ <td class="contentText_pad" id="bmstats_lcds2" colspan=3></td>
+ <script type="text/javascript">
+ setTooltip($("#lead_lcds2"), tooltipText_lcds2())</script>
+ </tr>
+</table>
+
+
+</td>
+
+<!-- *** END Selected time series stats ****************************** -->
+
+</tr>
+</table>
+<!-- *** END Main context and selected time series stats ************* -->
+
+<!-- *** BEGIN Time series benchmark and metric ********************** -->
+<br />
+<table style="border:0">
+ <tr>
+ <td style="border:0; padding-left:10px; text-align:right" class="benchmark">
+ <b>Benchmark:</b></td>
+ <td style="border:0; padding:2"><span id="benchmark"></span></td>
+ </tr>
+ <tr>
+ <td style="border:0; padding-left:10px; text-align:right" class="metric">
+ <b>Metric:</b></td>
+ <td style="border:0; padding:2"><span id="metric"></span></td>
+ </tr>
+</table>
+<!-- *** END Time series benchmark and metric ********************** -->
+
+<span id="status4">no status</span>
+<img alt="spinner4" id="spinner4" src="images/ajax-spinner.gif"
+ style="display:none"/>
+<img alt="nospinner4" id="nospinner4" src="images/nospinner.png"
+ style="display:inline"/>
+
+<!-- *** BEGIN Time series plot *********************************** -->
+<table style="border:0; background-color:#eee">
+ <tr>
+ <td style="border:0; padding-right:10px">Median<br />obs.</td>
+ <td style="border:0">
+ <div id="plot_canvas"
+ style="width:1000px; height:200px; background-color:#eee">
+ </div>
+ </td>
+ <td style="border:0; padding-left:10px">Sample<br />size</td>
+ </tr>
+ <tr>
+ <td style="border:0">
+ <div id="div_url_tsbm" style="display:inline">
+ <span style="text-align:left">
+ <a id="url_tsbm" onclick="return false"
+ href="javascript::void(0)">URL</a>
+ <script type="text/javascript">
+ setTooltip(
+ $("#url_tsbm"),
+ "Link to page with details for this time series.<br /><br />" +
+ "<b>Note:</b> Reloading that page will reflect the most" +
+ " recent results in the database.")
+ </script>
+ </span>
+ </div>
+ </td>
+ <td style="border:0; text-align:center; padding-top:0">
+ Days since report date
+ </td>
+ <td style="border:0">
+ </td>
+ </tr>
+</table>
+<!-- *** END Time series plot *********************************** -->
+
+<br />
+
+<a id="link_snapshotDetails" href="javascript::void(0)"
+ onclick="toggleSnapshotDetails()">show snapshot details</a>
+
+<!-- *** BEGIN Details of selected snapshot ****************************** -->
+<div id="div_snapshotDetails" style="display:none">
+<table style="border:0">
+ <tr>
+
+<!-- *** BEGIN Base result ****************************** -->
+
+ <td valign=top style="border:0; padding-right:20">
+ <table style="border:0">
+ <tr>
+ <td id="base_result" style="border:0; font-size:14; font-weight:bold">
+ Base result:
+ </td>
+ <script type="text/javascript">
+setTooltip(
+$("#base_result"),
+"The result at the most recent significant change <u>before</u> the selected " +
+"snapshot, or the first result of the time series if there are no " +
+"significant changes before the selected snapshot.");
+ </script>
+ </tr>
+ <tr>
+ <td class="leadText">SHA-1:</td>
+ <td id="base_res_sha1" class="contentText_mono"></td>
+ </tr>
+ <tr>
+ <td valign=top class="leadText" style="text-align:right">Sample:</td>
+ <td valign=top>
+ <div style="overflow:auto; height:170px; width:300px;">
+ <table id="sample1" class="tablesorter" border="0"
+ cellpadding="0" cellspacing="1" style="width:280px">
+ <thead>
+ <tr>
+ <th id="sample1_value">Value</th>
+ <script type="text/javascript">
+setTooltip(
+$("#sample1_value"), "Individual observations.<br />" +
+"The median of valid and positive observations is shown in " +
+"<span style=\"background-color:#ff0\">" +
+"yellow</span>.<br />Invalid observations are shown in " +
+"<span style=\"background-color:#faa\">red</span>.");
+ </script>
+ <th id="sample1_daysAgo">Days ago</th>
+ <script type="text/javascript">
+setTooltip(
+$("#sample1_daysAgo"),
+"Days ago since observation<br />was uploaded to the database.");
+ </script>
+ </tr>
+ </thead>
+ <tbody></tbody>
+ </table>
+ </div>
+ </td>
+ </tr>
+ <tr>
+ <td id="base_res_rse_lead" class="leadText">RSE:</td>
+ <script type="text/javascript">
+setTooltip($("#base_res_rse_lead"), tooltipText_rse_plot())
+ </script>
+ <td id="base_res_rse" class="contentText"></td>
+ </tr>
+ <tr>
+ <td id="base_res_medobs_lead" class="leadText">Median obs.:</td>
+ <script type="text/javascript">
+setTooltip($("#base_res_medobs_lead"), tooltipText_medobs_plot())
+ </script>
+ <td id="base_res_median_obs" class="contentText"></td>
+ </tr>
+ </table>
+ </td>
+
+<!-- *** END Preceding result ****************************** -->
+
+<!-- *** BEGIN Selected snapshot ****************************** -->
+
+ <td valign=top style="border:1; border-style:solid; border-color:#00f">
+ <table style="border:0">
+ <tr>
+ <td style="border:0; font-size:14; font-weight:bold">
+ Selected snapshot:</td>
+ </tr>
+ <tr>
+ <td class="leadText">SHA-1:</td>
+ <td id="curr_sshot_sha1" class="contentText_mono"></td>
+ </tr>
+ <tr>
+ <td valign=top class="leadText" style="text-align:right">Sample:</td>
+ <td valign=top>
+ <div style="overflow:auto; height:170px; width:300px;">
+ <table id="sample2" class="tablesorter" border="0"
+ cellpadding="0" cellspacing="1" style="width:280px">
+ <thead>
+ <tr>
+ <th id="sample2_value">Value</th>
+ <script type="text/javascript">
+setTooltip(
+$("#sample2_value"), "Individual observations.<br />" +
+"The median of valid and positive observations is shown in " +
+"<span style=\"background-color:#ff0\">" +
+"yellow</span>.<br />Invalid observations are shown in " +
+"<span style=\"background-color:#faa\">red</span>.");
+ </script>
+ <th id="sample2_daysAgo">Days ago</th>
+ <script type="text/javascript">
+setTooltip(
+$("#sample2_daysAgo"),
+"Days ago since observation<br />was uploaded to the database.");
+ </script>
+ </tr>
+ </thead>
+ <tbody></tbody>
+ </table>
+ </div>
+ </td>
+ </tr>
+ <tr>
+ <td id="curr_sshot_rse_lead" class="leadText">RSE:</td>
+ <script type="text/javascript">
+setTooltip($("#curr_sshot_rse_lead"), tooltipText_rse_plot())
+ </script>
+ <td id="curr_sshot_rse" class="contentText"></td>
+ </tr>
+ <tr>
+ <td id="curr_sshot_medobs_lead" class="leadText">Median obs.:</td>
+ <script type="text/javascript">
+setTooltip($("#curr_sshot_medobs_lead"), tooltipText_medobs_plot())
+ </script>
+ <td id="curr_sshot_median_obs" class="contentText"></td>
+ </tr>
+ <tr>
+ <td id="curr_sshot_change_lead" class="leadText">Change:</td>
+ <script type="text/javascript">
+setTooltip($("#curr_sshot_change_lead"), tooltipText_change_plot())
+ </script>
+ <td id="curr_sshot_change" class="contentText"></td>
+ </tr>
+ </table>
+ </td>
+
+<!-- *** END Selected snapshot ****************************** -->
+
+ </tr>
+</table>
+</div> <!-- div_snapshotDetails -->
+<!-- *** END Details of selected snapshot ****************************** -->
+
+</div> <!-- div_tsbm -->
diff --git a/web/analysis/tsbmbody.js b/web/analysis/tsbmbody.js
new file mode 100644
index 0000000..53d48a8
--- /dev/null
+++ b/web/analysis/tsbmbody.js
@@ -0,0 +1,1309 @@
+// --- BEGIN Global variables -----------------------------------
+var database = null;
+var currDate = null;
+
+var host = null;
+var platform = null;
+var branch = null;
+
+var snapshots = null;
+var plot = null;
+// --- END Global variables -----------------------------------
+
+function tooltipText_ms() {
+ return "Missing snapshots, i.e. the number of " +
+ "target snapshots for which no results exist." +
+ "<br /><br />A high value might indicate unstable execution.";
+}
+
+function tooltipText_lsd() {
+ return "Last Snapshot Distance, i.e. distance " +
+ "between the last target snapshot and " +
+ "the last snapshot in this time series." +
+ "<br /><br />If the last target snapshot is the " +
+ "last one available in the database, " +
+ "a high value might indicate that the benchmark " +
+ "currently fails to produce results.";
+}
+
+function tooltipText_ni() {
+ return "Total number of observations " +
+ "explicitly flagged as invalid.<br /><br />" +
+ "An invalid observation is typically " +
+ "caused by a failed QVERIFY() etc.";
+}
+
+function tooltipText_nz() {
+ return "Total number of non-positive observations.<br /><br />" +
+ "Normally an observation must be positive to be valid.<br />" +
+ "<b>Note:</b> A non-positive observation is not " +
+ "necessarily flagged as <i>invalid</i> (see NI).";
+}
+
+function tooltipText_nc() {
+ return "Number of significant changes, i.e. changes that are either " +
+ "better than <i>DT</i> or worse than 1 / <i>DT</i>, where <i>DT</i> " +
+ "is the difference tolerance.<br /><br />" +
+ "A high value might indicate unstable or fluctuating results.";
+}
+
+function tooltipText_mdrse() {
+ return "Median of the valid relative standard errors of all " +
+ "snapshots.<br /><br />" +
+ "&nbsp;&nbsp;&nbsp;&nbsp;" +
+ "<img src=\"images/rse.png\" />" +
+ "<br /><br />A high value might indicate " +
+ "unstable or fluctuating results.";
+}
+
+function tooltipText_rsemd() {
+ return "Relative standard error of the valid median observations " +
+ "of all snapshots.<br /><br />" +
+ "&nbsp;&nbsp;&nbsp;&nbsp;" +
+ "<img src=\"images/rse.png\" />" +
+ "<br /><br />A high value might indicate either 1)" +
+ "unstable or fluctuating results<br />" +
+ "or 2) stable changes of a high magnitude.";
+}
+
+function tooltipText_qs() {
+ return "Quality score for the benchmark:<br /><br />" +
+ "&nbsp;&nbsp;&nbsp;&nbsp;" +
+ "qs(LSD, NI, NZ, NC, MDRSE),<br /><br />" +
+ "where qs() computes the quality score as a number " +
+ "between 0 (good quality) and 1 (poor quality).";
+}
+
+function tooltipText_lc() {
+ return "Last significant change. <i>(See documentation elsewhere for a " +
+ "definition of how the significant changes of a time series are " +
+ "computed!)</i><br /><br />" +
+ "The higher the value is above 1, the more strongly it " +
+ "represents an improvement.<br />" +
+ "The lower the value is below 1, the more strongly it " +
+ "represents a regression.";
+}
+
+function tooltipText_lcda() {
+ return "Days ago (relative to the report date) since the first " +
+ "observation for the last significant change snapshot was uploaded " +
+ "to the database." +
+ "<br />The distance (in terms of number of target snapshots) " +
+ "between the last significant change snapshot and the last target " +
+ "snapshot is shown in parentheses.";
+}
+
+function tooltipText_lcda_nodist() {
+ return "Days ago (relative to the report date) since the first " +
+ "observation for the last significant change snapshot was uploaded " +
+ "to the database.";
+}
+
+function tooltipText_lcms() {
+ return "Magnitude score of the last significant change. This score " +
+ "indicates the strength of the last signicifant change as a value " +
+ "ranging from 0 (weak) to 1 (strong):<br /><br />" +
+ "&nbsp;&nbsp;&nbsp;&nbsp;" +
+ "<img src=\"images/lcms.png\" />";
+}
+
+function tooltipText_lcss() {
+ return "Stability score for the last significant change:<br /><br />" +
+ "&nbsp;&nbsp;&nbsp;&nbsp;" +
+ "(LCMS + LCGSS + LCLSS + LCDS1 + LCDS2) / 5<br /><br />" +
+ "The higher this score, the higher the likelihood that the last " +
+ "significant change is or will become permanent.";
+}
+
+function tooltipText_lcss1() {
+ return "Stability score for the last significant change that does not " +
+ "consider the history after the latter:<br /><br />" +
+ "&nbsp;&nbsp;&nbsp;&nbsp;" +
+ "(LCMS + LCGSS + LCLSS + LCDS1) / 4<br /><br />" +
+ "The higher this score, the higher the likelihood that the last " +
+ "signicifant change is or will become permanent, but since LCDS2 is " +
+ "omitted from the sum, a high LCSS1 is more likely to be caused " +
+ "by an outlier than a high LCSS!";
+}
+
+function tooltipText_lcgss() {
+ return "Global separation score for the last significant change. " +
+ "This score indicates how well the median observation " +
+ "at the last significant change snapshot is separated from the " +
+ "median observations at <u>all preceding</u> snapshots in the " +
+ "time series. The median observation at the base snapshot " +
+ "(i.e. the snapshot of the second to last significant change or " +
+ "the first snapshot in the time series) is used as the maximum " +
+ "separation reference." +
+ "<br />The score ranges from 0 (weak separation) to 1 " +
+ "(strong separation)." +
+ "<br /><br />This score roughly measures how close the median " +
+ "observation at the last significant change is to represent an " +
+ "\"all time high(low)\" up to this point in the history.";
+}
+
+function tooltipText_lclss() {
+ return "Local separation score for the last significant change. " +
+ "This score indicates how well the median observations on each side " +
+ "of the last significant change snapshot are separated from each " +
+ "other. Snapshots before the base snapshot (i.e. the snapshot of " +
+ "the second to last significant change or the first snapshot in the " +
+ "time series) are not considered. " +
+ "The median observation at the base snapshot is used as the maximum " +
+ "separation reference. " +
+ "<br />The score ranges from 0 (weak separation) to 1 " +
+ "(strong separation).";
+}
+
+function tooltipText_lcds1() {
+ return "Durability score 1 for the last significant change. " +
+ "This score indicates the distance (in terms of number of snapshots) " +
+ "from the last significant change to the base snapshot (i.e. the " +
+ "snapshot of the second to last significant change or the first " +
+ "snapshot in the time series)." +
+ "<br />The score ranges from 0 (weak durability) to 1 " +
+ "(strong durability), and is scaled against the min/max durability " +
+ "tolerances." +
+ "<br /><br />This score measures for how long the median " +
+ "observation stayed near the base value until the last significant " +
+ "change occurred.";
+}
+
+function tooltipText_lcds2() {
+ return "Durability score 2 for the last significant change. " +
+ "This score indicates the distance (in terms of number of snapshots) " +
+ "from the last significant change to the end of the time series." +
+ "<br />The score ranges from 0 (weak durability) to 1 " +
+ "(strong durability), and is scaled against the min/max durability " +
+ "tolerances." +
+ "<br /><br />This score measures for how long the median " +
+ "observation at the last significant change has stayed " +
+ "essentially the same.";
+}
+
+function tooltipText_rse_plot() {
+ return "Relative standard error of the valid and positive observations " +
+ "in this sample.<br /><br />" +
+ "<img src=\"images/rse.png\" /><br /><br />" +
+ "<b>Note:</b> RSE is not defined for less than two values.";
+}
+
+function tooltipText_medobs_plot() {
+ return "The median of the valid and positive observations in this sample." +
+ "<br /><br />This is often the most representative/typical value " +
+ "in the sample.";
+}
+
+function tooltipText_change_plot() {
+ return "The factor by which the selected result improves over " +
+ "the base result wrt the median observation." +
+ "<br /><br />The values 0.5, 1, and 2 indicate " +
+ "half, equal, and double performance respectively.";
+}
+
+// A Flot plugin to draw a horizontal age bar above a time series.
+// Snapshots are assumed to be regularly spaced.
+// ### 2 B DOCUMENTED!
+(function ($) {
+
+ var options = {
+ ageBar: {
+ // no options
+ }
+ };
+
+ function init(plot) {
+
+ function drawAgeBar(plot, ctx) {
+ //var opt = plot.getOptions().ageBar;
+
+ var currTime = dateToTimestamp(currDate);
+
+ var topGap = 5;
+ var y1 = -topGap;
+ var y2 = -20;
+ var nsnapshots = snapshots.length;
+ var w = plot.width() / (nsnapshots - 1);
+ var w_2 = w * 0.5;
+ var plotOffset = plot.getPlotOffset();
+
+ for (var i = 0; i < nsnapshots; ++i) {
+ var secsAgo = currTime - snapshots[i][1];
+ var color = ageColor(secsAgo);
+ var x1 = i * w - w_2;
+ var x2 = x1 + w;
+ x1 = Math.max(-w_2, x1);
+ x2 = Math.min(plot.width() + w_2, x2);
+
+ ctx.save();
+ ctx.translate(plotOffset.left, plotOffset.top);
+
+ ctx.fillStyle = color;
+ ctx.beginPath();
+ ctx.moveTo(x1, y1);
+ ctx.lineTo(x1, y2);
+ ctx.lineTo(x2, y2);
+ ctx.lineTo(x2, y1);
+ ctx.lineTo(x1, y1);
+ ctx.fill();
+
+ ctx.restore();
+ }
+ }
+
+ plot.hooks.drawOverlay.push(drawAgeBar);
+ }
+
+ $.plot.plugins.push({
+ init: init,
+ options: options,
+ name: "agebar",
+ version: "0.1"
+ });
+})(jQuery);
+
+function drawHighlightedSnapshot(
+ plot, ctx, index, precIndex, nsnapshots, color) {
+
+ if ((index < 0) || (index >= nsnapshots))
+ return;
+
+ if (nsnapshots < 0)
+ return;
+
+ var plotOffset = plot.getPlotOffset();
+ var w = plot.width() / (nsnapshots - 1);
+ var x = (index / (nsnapshots - 1.0)) * plot.width();
+ var x1 = -1;
+ if (precIndex >= 0)
+ x1 = x - ((index - precIndex) + 0.5) * w;
+ else
+ x1 = x - 0.5 * w;
+ var x2 = x + 0.5 * w;
+ var topExcess = 5;
+ var bottomExcess = 7;
+ var y1 = -topExcess;
+ var y2 = 0;
+ var y3 = plot.height();
+ var y4 = plot.height() + bottomExcess;
+
+ ctx.save();
+ ctx.translate(plotOffset.left, plotOffset.top);
+
+ ctx.fillStyle = color;
+ ctx.beginPath();
+ ctx.moveTo(x1, y1);
+ ctx.lineTo(x1, y2);
+ ctx.lineTo(x2, y2);
+ ctx.lineTo(x2, y1);
+ ctx.lineTo(x1, y1);
+ ctx.fill();
+
+ ctx.strokeStyle = color;
+ ctx.lineWidth = 1;
+ ctx.beginPath();
+ ctx.moveTo(x1, y2);
+ ctx.lineTo(x1, y3);
+ ctx.lineTo(x2, y3);
+ ctx.lineTo(x2, y2);
+ ctx.lineTo(x1, y2);
+ ctx.stroke();
+
+ ctx.fillStyle = color;
+ ctx.beginPath();
+ ctx.moveTo(x1, y3);
+ ctx.lineTo(x1, y4);
+ ctx.lineTo(x2, y4);
+ ctx.lineTo(x2, y3);
+ ctx.lineTo(x1, y3);
+ ctx.fill();
+
+ ctx.restore();
+}
+
+
+// A Flot plugin to manipulate highlighting of a hovered snapshot
+// in a time series. Snapshots are assumed to be regularly spaced.
+// ### 2 B DOCUMENTED!
+(function ($) {
+ var index = -1;
+ var precIndex = -1;
+
+ var options = {
+ hoveredSnapshot: {
+ size: -1,
+ color: "rgba(80, 80, 80, 0.3)"
+ }
+ };
+
+ function init(plot) {
+
+ plot.setHoveredSnapshot = function setHoveredSnapshot(i, pi) {
+ index = i;
+ precIndex = pi;
+ }
+
+ plot.clearHoveredSnapshot = function clearHoveredSnapshot() {
+ index = -1;
+ precIndex = -1;
+ }
+
+ function drawHoveredSnapshot(plot, ctx) {
+ var opt = plot.getOptions().hoveredSnapshot;
+ drawHighlightedSnapshot(
+ plot, ctx, index, precIndex, opt.size, opt.color);
+ }
+
+ plot.hooks.drawOverlay.push(drawHoveredSnapshot);
+ }
+
+ $.plot.plugins.push({
+ init: init,
+ options: options,
+ name: "hoveredsnapshot",
+ version: "0.1"
+ });
+})(jQuery);
+
+// A Flot plugin to manipulate highlighting of a clicked snapshot
+// in a time series. Snapshots are assumed to be regularly spaced.
+// ### 2 B DOCUMENTED!
+(function ($) {
+ var index = -1;
+ var precIndex = -1;
+
+ var options = {
+ clickedSnapshot: {
+ size: -1,
+ color: "rgba(0, 0, 255, 0.5)"
+ }
+ };
+
+ function init(plot) {
+
+ plot.setClickedSnapshot = function setClickedSnapshot(i, pi) {
+ index = i;
+ precIndex = pi;
+ }
+
+ plot.clearClickedSnapshot = function clearClickedSnapshot() {
+ index = -1;
+ precIndex = -1;
+ }
+
+ function drawClickedSnapshot(plot, ctx) {
+ var opt = plot.getOptions().clickedSnapshot;
+ drawHighlightedSnapshot(
+ plot, ctx, index, precIndex, opt.size, opt.color);
+ }
+
+ plot.hooks.drawOverlay.push(drawClickedSnapshot);
+ }
+
+ $.plot.plugins.push({
+ init: init,
+ options: options,
+ name: "clickedsnapshot",
+ version: "0.1"
+ });
+})(jQuery);
+
+// A Flot plugin to manipulate highlighting of missing snapshots
+// in a time series. Snapshots are assumed to be regularly spaced.
+// ### 2 B DOCUMENTED!
+(function ($) {
+ var missing = null;
+
+ var options = {
+ missingSnapshots: {
+ size: -1,
+ color: "rgba(255, 0, 0, 0.2)"
+ }
+ };
+
+ function init(plot) {
+
+ plot.setMissingSnapshots = function setMissingSnapshots(m) {
+ missing = m;
+ }
+
+ function drawMissingSnapshots(plot, ctx) {
+ if (missing == null) {
+ return;
+ }
+
+ var opt = plot.getOptions().missingSnapshots;
+
+ if (opt.size < 0) {
+ return;
+ }
+
+ var plotOffset = plot.getPlotOffset();
+ //var w_2 = 0.5 * (plot.width() / (opt.size - 1));
+ var w_2 = 2;
+
+ for (var i = 0; i < missing.length; ++i) {
+ var x = (missing[i] / (opt.size - 1.0)) * plot.width();
+ var x1 = x - w_2;
+ var x2 = x + w_2;
+ var y1 = 0;
+ var y2 = plot.height();
+
+ ctx.save();
+ ctx.translate(plotOffset.left, plotOffset.top);
+ ctx.strokeStyle = "rgba(0, 0, 0, 1)";
+ ctx.fillStyle = opt.color;
+ ctx.lineWidth = 1;
+
+ ctx.beginPath();
+ ctx.moveTo(x1, y1);
+ ctx.lineTo(x1, y2);
+ ctx.lineTo(x2, y2);
+ ctx.lineTo(x2, y1);
+ ctx.lineTo(x1, y1);
+ //ctx.stroke();
+ ctx.fill();
+
+ ctx.restore();
+ }
+ }
+
+ plot.hooks.drawOverlay.push(drawMissingSnapshots);
+ }
+
+ $.plot.plugins.push({
+ init: init,
+ options: options,
+ name: "missingsnapshots",
+ version: "0.1"
+ });
+})(jQuery);
+
+function clearPlot() {
+ plot = $.plot(
+ $("#plot_canvas"),
+ [
+ {
+ color: "#000",
+ data: null
+ }
+ ],
+ {
+ grid: {
+ show: false
+ }
+ }
+ );
+
+ $("#sample1 tr:gt(0)").remove();
+ $("#sample2 tr:gt(0)").remove();
+
+ $("#benchmark").text("");
+ $("#metric").text("");
+
+ $("#bmstats_ms").text("");
+ $("#bmstats_lsd").text("");
+ $("#bmstats_ni").text("");
+ $("#bmstats_nz").text("");
+ $("#bmstats_nc").text("");
+ $("#bmstats_mdrse").text("");
+ $("#bmstats_rsemd").text("");
+ $("#bmstats_qs").text("");
+ $("#bmstats_lc").text("");
+ $("#bmstats_lcda").text("");
+ $("#bmstats_lcms").text("");
+ $("#bmstats_lcss").text("");
+ $("#bmstats_lcss1").text("");
+ $("#bmstats_lcgss").text("");
+ $("#bmstats_lclss").text("");
+ $("#bmstats_lcds1").text("");
+ $("#bmstats_lcds2").text("");
+
+ $("#base_res_sha1").html(emptySHA1());
+ $("#base_res_rse").text("");
+ $("#base_res_median_obs").text("");
+
+ $("#curr_sshot_sha1").html(emptySHA1());
+ $("#curr_sshot_rse").text("");
+ $("#curr_sshot_median_obs").text("");
+ $("#curr_sshot_change").text("");
+
+ hideTSBMURL();
+}
+
+// Creates a plot that shows the time series and significant changes
+// for a single benchmark.
+function createPlot(
+ timeSeries, changes, benchmark, metric, lowerIsBetter, ms, lsd, ni, nz,
+ nc, mdrse, rsemd, qs, lc, lcda, lcd, lcms, lcss, lcss1, lcgss, lclss, lcds1,
+ lcds2) {
+
+ clearPlot();
+
+ var prevHoverIndex = -1;
+ var prevClickIndex = -1;
+
+ var maxSampleSize = null;
+ for (var i = 0; i < timeSeries.length; ++i) {
+ sampleSize = timeSeries[i][2];
+ if (i == 0) {
+ maxSampleSize = sampleSize;
+ } else {
+ maxSampleSize = Math.max(maxSampleSize, sampleSize);
+ }
+ }
+
+ var minVal = null;
+ var maxVal = null;
+ var tsValCoords = [];
+ var tsSampleSizeCoords = [];
+ var tsNRSECoords = []; // NRSE = Normalized Relative Standard Error
+ var tsNRSEVals = [];
+ var tsInvalidCoords = [];
+ var tsZeroCoords = [];
+ var isMissing = [];
+ for (i = 0; i < snapshots.length; ++i)
+ isMissing[isMissing.length] = true;
+ minMaxValSet = false;
+ for (var i = 0; i < timeSeries.length; ++i) {
+ index = timeSeries[i][0];
+ isMissing[index] = false;
+
+ val = timeSeries[i][1];
+ if (val > 0) {
+ tsValCoords[tsValCoords.length] = [index, val];
+ if (!minMaxValSet) {
+ minVal = maxVal = val;
+ minMaxValSet = true;
+ } else {
+ minVal = Math.min(minVal, val);
+ maxVal = Math.max(maxVal, val);
+ }
+ }
+
+ sampleSize = timeSeries[i][2];
+ tsSampleSizeCoords[tsSampleSizeCoords.length] = [index, sampleSize];
+ if (sampleSize > 1) {
+ var nrse = timeSeries[i][3];
+ if (val > 0)
+ tsNRSEVals[tsNRSEVals.length] = nrse;
+ scaledNRSE = nrse * maxSampleSize;
+ tsNRSECoords[tsNRSECoords.length] = [index, scaledNRSE];
+ } else if (val > 0) {
+ tsNRSEVals[tsNRSEVals.length] = -1;
+ }
+
+ invalidCount = timeSeries[i][4];
+ if (invalidCount > 0)
+ tsInvalidCoords[tsInvalidCoords.length] = [index, invalidCount];
+
+ zeroCount = timeSeries[i][5];
+ if (zeroCount > 0)
+ tsZeroCoords[tsZeroCoords.length] = [index, zeroCount];
+ }
+
+ var missing = [];
+ for (var i = 0; i < isMissing.length; ++i) {
+ if (isMissing[i]) {
+ missing[missing.length] = i;
+ }
+ }
+ plot.setMissingSnapshots(missing);
+
+ var goodChangeCoords = [];
+ var badChangeCoords = [];
+ for (i = 0; i < changes.length; ++i) {
+ tsIndex = changes[i][0];
+ val = changes[i][1];
+ if (val > 1) {
+ goodChangeCoords[goodChangeCoords.length] = tsValCoords[tsIndex];
+ } else {
+ badChangeCoords[badChangeCoords.length] = tsValCoords[tsIndex];
+ }
+ }
+
+ var currTime = dateToTimestamp(currDate);
+ var options = {
+ xaxis: {
+ min: 0,
+ max: snapshots.length - 1 ,
+ tickFormatter: function(x) {
+ var i = parseInt(x);
+ if ((i < 0) || (i >= snapshots.length))
+ return "";
+ var secsAgo = currTime - snapshots[i][1];
+ return secsToDays(secsAgo);
+ }
+ },
+ yaxis: {
+ min: minVal,
+ max: maxVal
+ },
+ y2axis: {
+ min: 0,
+ max: maxSampleSize,
+ tickDecimals: 0
+ },
+ grid: {
+ show: true,
+ hoverable: true,
+ clickable: true,
+ mouseActiveRadius: 5,
+ autoHighlight: true,
+ // backgroundColor: "#ffffff",
+ backgroundColor: null,
+ borderColor: "#ddd"
+ },
+ ageBar: {
+ // no options
+ },
+ hoveredSnapshot: {
+ size: snapshots.length
+ },
+ clickedSnapshot: {
+ size: snapshots.length
+ },
+ missingSnapshots: {
+ size: snapshots.length
+ },
+ lineWidth: 0
+ };
+
+ plot = $.plot(
+ $("#plot_canvas"),
+ [
+ {
+ hoverable: false,
+ clickable: false,
+ color: "#8ff",
+ bars: {
+ show: true,
+ align: "center",
+ lineWidth: 0,
+ fill: true
+ },
+ data: tsSampleSizeCoords,
+ yaxis: 2
+ },
+ {
+ hoverable: false,
+ clickable: false,
+ color: "#f22",
+ bars: {
+ show: true,
+ align: "center",
+ lineWidth: 0,
+ fill: true
+ },
+ data: tsZeroCoords,
+ yaxis: 2
+ },
+ {
+ hoverable: false,
+ clickable: false,
+ color: "#f0f",
+ bars: {
+ show: true,
+ align: "center",
+ lineWidth: 2,
+ fill: false
+ },
+ data: tsInvalidCoords,
+ yaxis: 2
+ },
+ {
+ hoverable: false,
+ clickable: false,
+ color: "#770",
+ lineWidth: 0,
+ points: {
+ show: true,
+ radius: 3,
+ lineWidth: 0,
+ fillColor: "#ff0",
+ fill: true
+ },
+ shadowSize: 0,
+ data: tsNRSECoords,
+ yaxis: 2
+ },
+ {
+ hoverable: false,
+ clickable: false,
+ color: "#000",
+ lines: {
+ show: true,
+ lineWidth: 1,
+ },
+ points: {
+ show: true,
+ radius: 2,
+ fill: false
+ },
+ shadowSize: 2,
+ data: tsValCoords
+ },
+ {
+ hoverable: false,
+ clickable: false,
+ color: "#0a0",
+ lines: {
+ show: false
+ },
+ points: {
+ show: true,
+ radius: 5,
+ fill: false
+ },
+ data: goodChangeCoords
+ },
+ {
+ hoverable: false,
+ clickable: false,
+ color: "#f00",
+ lines: {
+ show: false
+ },
+ points: {
+ show: true,
+ radius: 5,
+ fill: false
+ },
+ data: badChangeCoords
+ }
+ ],
+ options
+ );
+
+ function clearHoverHighlighting() {
+ plot.clearHoveredSnapshot();
+ plot.triggerRedrawOverlay();
+ prevHoverIndex = -1;
+ }
+
+ $("#plot_canvas").unbind("mouseleave");
+ $("#plot_canvas").bind("mouseleave", function (event, pos, item) {
+ clearHoverHighlighting();
+ });
+
+ $("#plot_canvas").unbind("plothover");
+ $("#plot_canvas").bind("plothover", function (event, pos, item) {
+ snIndex = Math.floor(pos.x + 0.5); // Snapshot index
+ if ((snIndex < 0) || (snIndex >= snapshots.length))
+ return;
+ if (snIndex == prevHoverIndex)
+ // No change, so just ignore:
+ return;
+ prevHoverIndex = snIndex;
+
+ var tsIndex = -1; // Time series index
+ for (tsIndex = 0; tsIndex < tsValCoords.length; ++tsIndex) {
+ if (tsValCoords[tsIndex][0] == snIndex)
+ break;
+ }
+ if (tsIndex == tsValCoords.length)
+ // The time series is missing the clicked snapshot:
+ tsIndex = -1;
+
+ plot.setHoveredSnapshot(snIndex, snIndex);
+ plot.triggerRedrawOverlay();
+
+ // $("#curr_sshot_sha1").text(snapshots[tsValCoords[tsIndex][0]][0]);
+ // $("#curr_sshot_median_obs").text(tsValCoords[tsIndex][1]);
+
+ // if (tsIndex > 0) {
+ // var val1 = tsValCoords[tsIndex - 1][1];
+ // var val2 = tsValCoords[tsIndex][1];
+ // if (lowerIsBetter) {
+ // ratio = val1 / val2;
+ // } else {
+ // ratio = val2 / val1;
+ // }
+ // $("#base_res_median_obs").text(val1);
+ // $("#curr_sshot_change").text(ratio);
+
+ // sha1 = snapshots[tsValCoords[tsIndex][0]][0];
+ // sha1_prec = snapshots[tsValCoords[tsIndex - 1][0]][0];
+ // fetchResultDetails2(benchmark, metric, sha1_prec, sha1);
+
+ // } else {
+ // $("#base_res_median_obs").text("none");
+ // $("#curr_sshot_change").text("");
+ // }
+ });
+
+
+ function clearClickHighlighting() {
+ plot.clearClickedSnapshot();
+ plot.triggerRedrawOverlay();
+ prevClickIndex = -1;
+
+ $("#base_res_sha1").html(emptySHA1());
+ $("#base_res_rse").text("");
+ $("#base_res_median_obs").text("");
+
+ $("#curr_sshot_sha1").html(emptySHA1());
+ $("#curr_sshot_rse").text("");
+ $("#curr_sshot_median_obs").text("");
+ $("#curr_sshot_change").text("");
+
+ $("#sample1 tr:gt(0)").remove();
+ $("#sample2 tr:gt(0)").remove();
+ }
+
+ $("#plot_canvas").unbind("plotclick");
+ $("#plot_canvas").bind("plotclick", function (event, pos, item) {
+ snIndex = Math.floor(pos.x + 0.5); // Snapshot index
+ if ((snIndex < 0) || (snIndex >= snapshots.length))
+ return;
+ if (snIndex == prevClickIndex) {
+ // No change ...
+ clearClickHighlighting();
+ return;
+ }
+ prevClickIndex = snIndex;
+
+ var tsIndex = -1; // Time series index
+ for (tsIndex = 0; tsIndex < tsValCoords.length; ++tsIndex) {
+ if (tsValCoords[tsIndex][0] == snIndex)
+ break;
+ }
+ if (tsIndex == tsValCoords.length)
+ // The time series is missing the clicked snapshot:
+ tsIndex = -1;
+
+ plot.setClickedSnapshot(snIndex, snIndex);
+ plot.triggerRedrawOverlay();
+
+ $("#base_res_sha1").html(emptySHA1());
+ $("#base_res_rse").text("");
+ $("#base_res_median_obs").text("");
+
+ $("#curr_sshot_sha1").text(snapshots[snIndex][0]);
+ $("#curr_sshot_rse").text("");
+ $("#curr_sshot_median_obs").text("");
+ $("#curr_sshot_change").text("");
+
+ $("#sample1 tr:gt(0)").remove();
+ $("#sample2 tr:gt(0)").remove();
+
+ if (tsIndex >= 0) {
+
+ $("#curr_sshot_median_obs").text(tsValCoords[tsIndex][1]);
+ var nrse = tsNRSEVals[tsIndex];
+ if (nrse >= 0)
+ $("#curr_sshot_rse").text((nrse * 100).toFixed(2));
+ else
+ $("#curr_sshot_rse").text("");
+
+ if (tsIndex > 0) {
+
+ var i;
+ var baseIndex = 0;
+ for (i = 0; i < changes.length; ++i) {
+ changeIndex = changes[i][0];
+ if (tsIndex <= changeIndex)
+ break;
+ baseIndex = changeIndex;
+ }
+
+ var baseVal = tsValCoords[baseIndex][1];
+ var changeVal = tsValCoords[tsIndex][1];
+ if (lowerIsBetter)
+ ratio = baseVal / changeVal;
+ else
+ ratio = changeVal / baseVal;
+
+ var baseTsIndex = tsValCoords[baseIndex][0];
+ $("#base_res_sha1").text(snapshots[baseTsIndex][0]);
+
+ $("#base_res_median_obs").text(baseVal);
+ var nrse = tsNRSEVals[baseIndex];
+ if (nrse >= 0)
+ $("#base_res_rse").text((nrse * 100).toFixed(2));
+ else
+ $("#base_rse_rse").text("");
+ $("#curr_sshot_change").text(ratio);
+ $("#curr_sshot_change").css(
+ "color", (ratio > 1) ? "#0a0"
+ : ((ratio < 1) ? "f00" : "000"));
+
+ var sha1 = snapshots[tsValCoords[tsIndex][0]][0];
+ var sha1_prec = snapshots[tsValCoords[baseIndex][0]][0];
+ fetchResultDetails2(benchmark, metric, sha1_prec, sha1);
+ } else {
+ var sha1 = snapshots[tsValCoords[tsIndex][0]][0];
+ fetchResultDetails2(benchmark, metric, "", sha1);
+ }
+ }
+ });
+
+ clearHoverHighlighting();
+ clearClickHighlighting();
+
+ $("#benchmark").html(benchmark.replace(anySpace, "&nbsp;"));
+ $("#metric").text(metric);
+ if (isNonNullNumber(ms)) $("#bmstats_ms").text(ms);
+ if (isNonNullNumber(lsd)) $("#bmstats_lsd").text(lsd);
+ if (isNonNullNumber(ni)) $("#bmstats_ni").text(ni);
+ if (isNonNullNumber(nz)) $("#bmstats_nz").text(nz);
+ if (isNonNullNumber(nc)) $("#bmstats_nc").text(nc);
+ if (isNonNullNumber(mdrse))
+ $("#bmstats_mdrse").text(parseFloat(mdrse).toFixed(2));
+ if (isNonNullNumber(rsemd))
+ $("#bmstats_rsemd").text(parseFloat(rsemd).toFixed(2));
+ if (isNonNullNumber(qs))
+ $("#bmstats_qs").text(qs);
+ if (isNonNullNumber(lc)) {
+ $("#bmstats_lc").text(lc);
+ $("#bmstats_lc").css(
+ "color", ((lc < 1) ? "#a00" : ((lc > 1) ? "#0a0" : "#000")));
+ }
+ if (isNonNullNumber(lcda)) {
+ $("#bmstats_lcda").html(
+ parseFloat(lcda).toFixed(2) + "&nbsp;(" + lcd + ")");
+ var secsAgo = daysToSecs(lcda);
+ var ageColor_ = ageColor(secsAgo);
+ $("#bmstats_lcda").css("background-color", ageColor_);
+ }
+ if (isNonNullNumber(lcms)) $("#bmstats_lcms").text(lcms);
+ if (isNonNullNumber(lcss)) $("#bmstats_lcss").text(lcss);
+ if (isNonNullNumber(lcss1)) $("#bmstats_lcss1").text(lcss1);
+ if (isNonNullNumber(lcgss)) $("#bmstats_lcgss").text(lcgss);
+ if (isNonNullNumber(lclss)) $("#bmstats_lclss").text(lclss);
+ if (isNonNullNumber(lcds1)) $("#bmstats_lcds1").text(lcds1);
+ if (isNonNullNumber(lcds2)) $("#bmstats_lcds2").text(lcds2);
+}
+
+// ### REFACTOR: Similar function in stats2.js! 2 B DONE!
+function fetchResultDetails2(benchmark, metric, sha11, sha12) {
+ updateStatus("fetching result details ...", true);
+
+ query = "?db=" + database +
+ "&cmd=result_details2" +
+ "&host1=" + encodeURIComponent(host) +
+ "&platform1=" + encodeURIComponent(platform) +
+ "&branch1=" + encodeURIComponent(branch) +
+ "&sha11=" + sha11 +
+ "&host2=" + encodeURIComponent(host) +
+ "&platform2=" + encodeURIComponent(platform) +
+ "&branch2=" + encodeURIComponent(branch) +
+ "&sha12=" + sha12 +
+ "&benchmark=" + encodeURIComponent(benchmark) +
+ "&metric=" + encodeURIComponent(metric);
+
+ url = "http://" + location.host + "/cgi-bin/getstatswrapper" + query;
+ //alert("url: >" + url + "<");
+
+ $.ajax({
+ url: url,
+ type: "GET",
+ dataType: "json",
+
+ success: function(data, textStatus, request) {
+ if (request.readyState == 4) {
+ if (request.status == 200) {
+
+ if (data.error != null) {
+ updateStatus(
+ "fetching result details ... failed: " +
+ data.error, false);
+ return;
+ }
+
+ updateStatus("fetching result details ... done", false);
+ updateStatus("", false);
+
+ // Show samples ...
+ samples = { "1": data.sample1, "2": data.sample2 };
+ var currTime = dateToTimestamp(currDate);
+ for (key in samples) {
+
+ // $("#sample" + key + " option").remove();
+
+ sample = samples[key];
+ nValidAndPos = 0;
+ for (i = 0; i < sample.length; ++i)
+ nValidAndPos += (
+ sample[i].valid && sample[i].value > 0);
+ medianIndex = Math.floor(nValidAndPos / 2);
+ twoMedianIndexes = ((nValidAndPos % 2) == 0);
+ validIndex = 0;
+ html = "";
+ for (i = 0; i < sample.length; ++i) {
+ value = sample[i].value;
+ valid = sample[i].valid;
+ secsAgo = currTime - sample[i].timestamp;
+ ageColor_ = ageColor(secsAgo);
+
+ if (!valid) {
+ bgColor = "#faa";
+ } else {
+ if ((validIndex == medianIndex) ||
+ (twoMedianIndexes
+ && (validIndex == (medianIndex - 1)))) {
+ bgColor = "#ff0";
+ } else {
+ bgColor = "#fff";
+ }
+ validIndex++;
+ }
+
+ // $("#sample" + key).append(
+ // "<option value=\"" + value +
+ // "\" style=\"background-color:" +
+ // bgColor + "\">" + value + "</option>");
+
+ html += "<tr>";
+ html += "<td style=\"background-color:" +
+ bgColor + "; text-align:right\">" +
+ value + "</td>";
+ html += "<td style=\"background-color:" +
+ ageColor_ + "; text-align:right\">" +
+ secsToDays(secsAgo) + "</td>";
+ html += "</tr>";
+ }
+
+ // Update table:
+ sel = "#sample" + key;
+ $(sel + " tr:gt(0)").remove();
+ $(sel + " > tbody:last").append(html);
+ $(sel).trigger("update");
+ //$(sel).trigger("appendCache");
+ }
+ }
+ }
+ },
+
+ error: function(request, textStatus, errorThrown) {
+ descr = errorThrown;
+ if (errorThrown == null) {
+ descr = "undefined error - is the server down?";
+ }
+ updateStatus(
+ "fetching result details ... error: " + descr, false);
+ }
+
+ // complete: function(request, textStatus) {
+ // alert("complete; request.status: " + request.status)
+ // }
+
+ });
+}
+
+function fetchTimeSeries(
+ database, host, platform, branch, sha11, sha12, benchmark, metric, difftol,
+ durtolmin, durtolmax, showTSBMURL) {
+
+ hideTSBMURL();
+
+ updateStatus("fetching time series ...", true);
+
+ query = "?db=" + database +
+ "&cmd=timeseriesdetails" +
+ "&host=" + encodeURIComponent(host) +
+ "&platform=" + encodeURIComponent(platform) +
+ "&branch=" + encodeURIComponent(branch) +
+ "&sha11=" + sha11 +
+ "&sha12=" + sha12 +
+ "&benchmark=" + encodeURIComponent(benchmark) +
+ "&metric=" + encodeURIComponent(metric) +
+ "&difftol=" + difftol +
+ "&durtolmin=" + durtolmin +
+ "&durtolmax=" + durtolmax;
+
+ url = "http://" + location.host + "/cgi-bin/getstatswrapper" + query;
+ //alert("url: >" + url + "<");
+
+ $.ajax({
+ url: url,
+ type: "GET",
+ dataType: "json",
+
+ success: function(data, textStatus, request) {
+ if (request.readyState == 4) {
+ if (request.status == 200) {
+ if (data.error != null) {
+ updateStatus(
+ "fetching time series ... failed: " +
+ data.error, false);
+ return;
+ }
+
+ updateStatus("fetching time series ... done", false);
+ updateStatus("", false);
+
+ var ms = data.ms;
+ var lsd = data.lsd;
+ var ni = null;
+ var nz = null;
+ var mdrse = null;
+ var rsemd = null;
+ var qs = null;
+ var lc = null;
+ var lcda = null;
+ var lcd = null;
+ var lcms = null;
+ var lcss = null;
+ var lcss1 = null;
+ var lcgss = null;
+ var lclss = null;
+ var lcds1 = null;
+ var lcds2 = null;
+
+ if (lsd >= 0) {
+ ni = data.ni;
+ nz = data.nz;
+ nc = data.nc;
+ mdrse = data.med_of_rses;
+ rsemd = data.rse_of_meds;
+ qs = qualityScore(lsd, ni, nz, nc, mdrse);
+ if (nc > 0) {
+ lc = data.lc;
+ var currTime = dateToTimestamp(currDate);
+ var secsAgo = currTime - data.lc_timestamp;
+ lcda = secsToDays(secsAgo);
+ lcd = data.lc_distance;
+
+ lcms = changeMagnitudeScore(lc);
+ lcgss = parseFloat(data.lc_gsep_score);
+ lclss = parseFloat(data.lc_lsep_score);
+ lcds1 = parseFloat(data.lc_dur1_score);
+ lcds2 = parseFloat(data.lc_dur2_score);
+
+ lcss = lcms * lcgss * lclss * lcds1 * lcds2;
+ lcss1 = lcms * lcgss * lclss * lcds1;
+ }
+ }
+
+ createPlot(
+ data.time_series, data.changes, benchmark, metric,
+ parseInt(data.lib), ms, lsd, ni, nz, nc, mdrse, rsemd,
+ qs, lc, lcda, lcd, lcms, lcss, lcss1, lcgss, lclss,
+ lcds1, lcds2);
+
+ if (showTSBMURL)
+ enableTSBMURL(
+ database, host, platform, branch, sha11, sha12,
+ benchmark, metric, difftol, durtolmin, durtolmax);
+ }
+ }
+ },
+
+ error: function(request, textStatus, errorThrown) {
+ descr = errorThrown;
+ if (errorThrown == null) {
+ descr = "undefined error - is the server down?";
+ }
+ updateStatus("fetching time series ... error: " + descr, false);
+ }
+
+ // complete: function(request, textStatus) {
+ // alert("complete; request.status: " + request.status)
+ // }
+
+ });
+}
+
+function hideTSBMURL() {
+ sel = $("#url_tsbm");
+ sel.css("display", "none");
+}
+
+function disableTSBMURL() {
+ sel = $("#url_tsbm");
+ sel.attr("onclick", "return false");
+ sel.attr("href", "javascript::void(0)");
+}
+
+function enableTSBMURL(
+ database, host, platform, branch, sha11, sha12, benchmark, metric, difftol,
+ durtolmin, durtolmax) {
+
+ query = "?db=" + database;
+ query += "&host=" + encodeURIComponent(host);
+ query += "&platform=" + encodeURIComponent(platform);
+ query += "&branch=" + encodeURIComponent(branch);
+ query += "&sha11=" + sha11;
+ query += "&sha12=latest"; // invalid SHA-1 specifies infinite endpoint
+ query += "&benchmark=" + encodeURIComponent(benchmark);
+ query += "&metric=" + encodeURIComponent(metric);
+ query += "&difftol=" + difftol;
+ query += "&durtolmin=" + durtolmin;
+ query += "&durtolmax=" + durtolmax;
+
+ url = "http://" + location.host + "/bm2/analysis/tsbm.shtml" + query;
+
+ sel = $("#url_tsbm");
+ sel.removeAttr("onclick");
+ sel.attr("href", url);
+ sel.css("display", "block");
+}
+
+function initTSBMBody() {
+ $("#sample1").tablesorter({
+ headers: {
+ 0: { sorter: "mixed_numeric_desc_before_missing" },
+ 1: { sorter: "mixed_numeric_desc_before_missing" }
+ }
+ });
+
+ $("#sample2").tablesorter({
+ headers: {
+ 0: { sorter: "mixed_numeric_desc_before_missing" },
+ 1: { sorter: "mixed_numeric_desc_before_missing" }
+ }
+ });
+
+ hideTSBMURL();
+
+ currDate = new Date();
+ $("#main_context_date").text(
+ currDate.toDateString() + " " + currDate.toTimeString());
+}
+
+function updateMainContextInterval(sshot1, sshot2, nsnapshots) {
+ var currTime = dateToTimestamp(currDate);
+ $("#main_context_nsnapshots").text(nsnapshots);
+ var secsAgo1 = currTime - sshot1[1];
+ $("#main_context_sha11_daysago").text(secsToDays(secsAgo1));
+ $("#main_context_sha11_daysago").css("backgroundColor", ageColor(secsAgo1));
+ var secsAgo2 = currTime - sshot2[1];
+ $("#main_context_sha12_daysago").text(secsToDays(secsAgo2));
+ $("#main_context_sha12_daysago").css("backgroundColor", ageColor(secsAgo2));
+}
+
+function setSnapshots(sshots) {
+ snapshots = sshots;
+ updateMainContextInterval(
+ sshots[0], sshots[sshots.length - 1], sshots.length);
+}
+
+// Handles clicking a benchmark radio button.
+function clickBMRadioButton(
+ cb, tableSel, database, host, platform, branch, sha11, sha12, benchmark,
+ metric, difftol, durtolmin, durtolmax) {
+
+ if (cb.checked) {
+ // Fetch and plot time series:
+ fetchTimeSeries(
+ database, host, platform, branch, sha11, sha12, benchmark, metric,
+ difftol, durtolmin, durtolmax, true);
+ } else {
+ clearPlot();
+ }
+}
+
+
+function toggleSnapshotDetails() {
+ var linkObj = $("#link_snapshotDetails");
+ var divObj = $("#div_snapshotDetails");
+ var curr = divObj.css("display");
+ if (curr == "none") {
+ linkObj.text("hide snapshot details");
+ divObj.css("display", "block");
+ } else {
+ linkObj.text("show snapshot details");
+ divObj.css("display", "none");
+ }
+}
diff --git a/web/analysis/tspbmtabledef.html b/web/analysis/tspbmtabledef.html
new file mode 100644
index 0000000..6f1ba92
--- /dev/null
+++ b/web/analysis/tspbmtabledef.html
@@ -0,0 +1,26 @@
+<thead>
+<tr>
+ <th></th> <!-- details checkbox -->
+ <th>MS</th>
+ <th>LSD</th>
+ <th>NI</th>
+ <th>NZ</th>
+ <th>NC</th>
+ <th>MDRSE</th>
+ <th>RSEMD</th>
+ <th>QS</th>
+ <th>LC</th>
+ <th>LCDA</th>
+ <th>LCMS</th>
+ <th>LCSS</th>
+ <th>LCSS1</th>
+ <th>LCGSS</th>
+ <th>LCLSS</th>
+ <th>LCDS1</th>
+ <th>LCDS2</th>
+ <th>Metric</th>
+ <th>Benchmark</th>
+</tr>
+</thead>
+<tbody>
+</tbody>
diff --git a/web/analysis/tsstats.js b/web/analysis/tsstats.js
new file mode 100644
index 0000000..b659752
--- /dev/null
+++ b/web/analysis/tsstats.js
@@ -0,0 +1,392 @@
+// Handles selecting a per-benchmark table.
+function selectPbmTable() {
+ var val = $("#select_pbmTable").attr("value");
+ var types = ["all", "lcRegr", "lcImpr"];
+ for (index in types) {
+ var type = types[index];
+ $("#div_pbmTable_" + type).css(
+ "display", (val == type) ? "block" : "none");
+ }
+
+ var nrows = $("#pbmTable_" + val).find("tr").length - 1;
+ $("#pbmTable_nrows").text(nrows + ((nrows == 1) ? " row" : " rows"));
+}
+
+function changeMagnitudeScore(change) {
+ var maxChange = 2.0;
+ var absChange = (change < 1.0) ? (1.0 / change) : change;
+ return (Math.min(absChange, maxChange) - 1.0) / (maxChange - 1.0);
+}
+
+function populatePbmTable(tableSel, data) {
+
+ // Remove all rows below the header ...
+ $(tableSel + " tr:gt(0)").remove();
+
+ pbm_stats = data.per_bm_stats;
+
+ var currTime = dateToTimestamp(currDate);
+
+ var html = "";
+ for (var i = 0; i < pbm_stats.length; ++i) {
+
+ stats = pbm_stats[i];
+
+ var lsd = stats.lsd;
+ var nc = null;
+ var lc = null;
+ if (lsd >= 0) {
+ nc = stats.nc;
+ if (nc > 0)
+ lc = stats.lc;
+ }
+ if (tableSel == "#pbmTable_lcRegr") {
+ if ((lc == null) || (lc >= 1))
+ continue;
+ } else if (tableSel == "#pbmTable_lcImpr") {
+ if ((lc == null) || (lc <= 1))
+ continue;
+ }
+
+ // NOTE: Unused for now:
+ //lowerIsBetter = stats.lib;
+
+ var ms = null;
+ var ni = null;
+ var nz = null;
+ var mdrse = null;
+ var rsemd = null;
+ var qs = null;
+ var lcda = null;
+ var lcd = null;
+ var lcms = null;
+ var lcss = null;
+ var lcss1 = null;
+ var lcgss = null;
+ var lclss = null;
+ var lcds1 = null;
+ var lcds2 = null;
+
+ var rbdy = "";
+
+ var ms = stats.ms;
+ rbdy += "<td>" + ms + "</td>";
+
+ if (lsd >= 0) {
+ rbdy += "<td>" + lsd + "</td>";
+
+ ni = stats.ni;
+ rbdy += "<td>" + ni + "</td>";
+
+ nz = stats.nz;
+ rbdy += "<td>" + nz + "</td>";
+
+ nc = stats.nc;
+ rbdy += "<td>" + nc + "</td>";
+
+ mdrse = stats.med_of_rses;
+ if (mdrse >= 0) {
+ rbdy += "<td style=\"text-align:right\">" +
+ mdrse.toFixed(2) + "</td>";
+ } else {
+ rbdy += "<td></td>";
+ }
+
+ rsemd = stats.rse_of_meds;
+ if (rsemd >= 0) {
+ rbdy += "<td style=\"text-align:right\">" +
+ rsemd.toFixed(2) + "</td>";
+ } else {
+ rbdy += "<td></td>";
+ }
+
+ qs = qualityScore(lsd, ni, nz, nc, mdrse);
+ rbdy += "<td>" + qs.toFixed(4) + "</td>";
+
+ if (nc > 0) {
+
+ rbdy += "<td style=\"text-align:right; color:" +
+ ((lc < 1) ? "#a00" : ((lc > 1) ? "#0a0" : "#000")) +
+ "\">" + lc.toFixed(4) + "</td>";
+
+ var secsAgo = currTime - stats.lc_timestamp;
+ lcda = secsToDays(secsAgo);
+ lcd = stats.lc_distance;
+ rbdy += "<td style=\"background-color:" + ageColor(secsAgo) +
+ "; text-align:right\">" + lcda + "&nbsp;(" + lcd + ")" +
+ "</td>";
+
+ lcms = changeMagnitudeScore(lc);
+ rbdy += "<td>" + lcms.toFixed(4) + "</td>";
+
+ lcgss = stats.lc_gsep_score;
+ lclss = stats.lc_lsep_score;
+ lcds1 = stats.lc_dur1_score;
+ lcds2 = stats.lc_dur2_score;
+
+ lcss = (lcms + lcgss + lclss + lcds1 + lcds2) / 5.0;
+ rbdy += "<td>" + lcss + "</td>";
+
+ lcss1 = (lcms + lcgss + lclss + lcds1) / 4.0;
+ rbdy += "<td>" + lcss1 + "</td>";
+
+ rbdy += "<td>" + lcgss.toFixed(4) + "</td>";
+ rbdy += "<td>" + lclss.toFixed(4) + "</td>";
+ rbdy += "<td>" + lcds1.toFixed(4) + "</td>";
+ rbdy += "<td>" + lcds2.toFixed(4) + "</td>";
+
+ } else {
+ rbdy += "<td></td>"; // LC
+ rbdy += "<td></td>"; // LCDA
+ rbdy += "<td></td>"; // LCMS
+ rbdy += "<td></td>"; // LCSS
+ rbdy += "<td></td>"; // LCSS1
+ rbdy += "<td></td>"; // LCGSS
+ rbdy += "<td></td>"; // LCLSS
+ rbdy += "<td></td>"; // LCDS1
+ rbdy += "<td></td>"; // LCDS2
+ }
+ } else {
+ rbdy += "<td></td>"; // LSD
+ rbdy += "<td></td>"; // NI
+ rbdy += "<td></td>"; // NZ
+ rbdy += "<td></td>"; // NC
+ rbdy += "<td></td>"; // MDRSE
+ rbdy += "<td></td>"; // RSEMD
+ rbdy += "<td></td>"; // QS
+ rbdy += "<td></td>"; // LC
+ rbdy += "<td></td>"; // LCDA
+ rbdy += "<td></td>"; // LCMS
+ rbdy += "<td></td>"; // LCSS
+ rbdy += "<td></td>"; // LCSS1
+ rbdy += "<td></td>"; // LCGSS
+ rbdy += "<td></td>"; // LCLSS
+ rbdy += "<td></td>"; // LCDS1
+ rbdy += "<td></td>"; // LCDS2
+ }
+
+ rbdy = "<td><input name=\"currSelector\" type=\"radio\" onclick=\"" +
+ "clickBMRadioButton(this, '" + tableSel + "', '" +
+ data.database + "', '" + data.host + "', '" +
+ data.platform + "', '" + data.branch + "', '" +
+ data.sha11 + "', '" + data.sha12 + "', '" +
+ stats.bm + "', '" + stats.mt + "', " +
+ data.difftol + ", " + data.durtolmin + ", " +
+ data.durtolmax +
+ ")\"></td>" +
+ rbdy;
+
+ rbdy += "<td class=\"metric\">" + stats.mt + "</td>";
+ rbdy += "<td class=\"benchmark\">" +
+ stats.bm.replace(anySpace, "&nbsp;") + "</td>";
+
+ html += "<tr>" + rbdy + "</tr>";
+ }
+
+ $(tableSel + " > tbody:last").append(html);
+ $(tableSel).trigger("update");
+ if (html != "") // hm ... why is this test necessary?
+ $(tableSel).trigger("appendCache");
+
+ // var sorting = [[11,1],[0,0]];
+ //$("table").trigger("sorton",[sorting]);
+}
+
+function fetchStats(
+ database, host, platform, branch, sha11, sha12, difftol, durtolmin,
+ durtolmax, testCaseFilter) {
+ updateStatus("fetching statistics ...", true);
+
+ query = "?db=" + database +
+ "&cmd=timeseriesstats" +
+ "&host=" + encodeURIComponent(host) +
+ "&platform=" + encodeURIComponent(platform) +
+ "&branch=" + encodeURIComponent(branch) +
+ "&sha11=" + sha11 +
+ "&sha12=" + sha12;
+ if (difftol != "")
+ query += "&difftol=" + difftol;
+ if (durtolmin != "")
+ query += "&durtolmin=" + durtolmin;
+ if (durtolmax != "")
+ query += "&durtolmax=" + durtolmax;
+ if (testCaseFilter != "")
+ query += "&testcasefilter=" + encodeURIComponent(testCaseFilter);
+
+ url = "http://" + location.host + "/cgi-bin/getstatswrapper" + query;
+ //alert("url: >" + url + "<");
+
+ $.ajax({
+ url: url,
+ type: "GET",
+ dataType: "json",
+
+ success: function(data, textStatus, request) {
+ if (request.readyState == 4) {
+ if (request.status == 200) {
+
+ if (data.error != null) {
+ updateStatus(
+ "fetching statistics ... failed: " +
+ data.error, false);
+ return;
+ }
+
+ updateStatus("fetching statistics ... done", false);
+ updateStatus("", false);
+
+ // Show context ...
+ $("#main_context_database").text(data.database);
+ $("#main_context_host").text(data.host);
+ $("#main_context_platform").text(data.platform);
+ $("#main_context_branch").text(data.branch);
+ $("#main_context_sha11").text(data.sha11);
+ $("#main_context_sha12").text(data.sha12);
+ $("#main_context_difftol").text(data.difftol);
+ $("#main_context_durtolmin").text(data.durtolmin);
+ $("#main_context_durtolmax").text(data.durtolmax);
+
+ setSnapshots(data.snapshots);
+
+ populatePbmTable("#pbmTable_all", data);
+ populatePbmTable("#pbmTable_lcRegr", data);
+ populatePbmTable("#pbmTable_lcImpr", data);
+
+ // Initially show the table with all benchmarks:
+ $("#select_pbmTable").attr("value", "all");
+ selectPbmTable();
+
+ $("#div_tsbm_border").css("display", "block");
+ $("#div_tsbm").css("display", "block");
+ $("#div_perBenchmarkStats").css("display", "block");
+
+ clearPlot();
+ }
+ }
+ },
+
+ error: function(request, textStatus, errorThrown) {
+ descr = errorThrown;
+ if (errorThrown == null) {
+ descr = "undefined error - is the server down?";
+ }
+ updateStatus("fetching statistics ... error: " + descr, false);
+ }
+
+ // complete: function(request, textStatus) {
+ // alert("complete; request.status: " + request.status)
+ // }
+
+ });
+}
+
+function initPbmTable(tableSel) {
+ $(tableSel).tablesorter({
+ headers: {
+ 0: { sorter: false }, // checkbox
+ 1: { sorter: "mixed_numeric_desc_before_missing" }, // MS
+ 2: { sorter: "mixed_numeric_desc_before_missing" }, // LSD
+ 3: { sorter: "mixed_numeric_desc_before_missing" }, // NI
+ 4: { sorter: "mixed_numeric_desc_before_missing" }, // NZ
+ 5: { sorter: "mixed_numeric_desc_before_missing" }, // NC
+ 6: { sorter: "mixed_numeric_desc_before_missing" }, // MDRSE
+ 7: { sorter: "mixed_numeric_desc_before_missing" }, // RSEMD
+ 8: { sorter: "mixed_numeric_desc_before_missing" }, // QS
+ 9: { sorter: "mixed_numeric_desc_before_missing" }, // LC
+ 10: { sorter: "mixed_numeric_asc_before_missing" }, // LCDA
+ 11: { sorter: "mixed_numeric_desc_before_missing" }, // LCMS
+ 12: { sorter: "mixed_numeric_desc_before_missing" }, // LCSS
+ 13: { sorter: "mixed_numeric_desc_before_missing" }, // LCSS1
+ 14: { sorter: "mixed_numeric_desc_before_missing" }, // LCGSS
+ 15: { sorter: "mixed_numeric_desc_before_missing" }, // LCLSS
+ 16: { sorter: "mixed_numeric_desc_before_missing" }, // LCDS1
+ 17: { sorter: "mixed_numeric_desc_before_missing" } // LCDS2
+ }
+ });
+
+ // Note: The nth-child selector below uses 1-based indexing!
+ setTooltip($(tableSel).find("th:nth-child(2)"), tooltipText_ms());
+ setTooltip($(tableSel).find("th:nth-child(3)"), tooltipText_lsd());
+ setTooltip($(tableSel).find("th:nth-child(4)"), tooltipText_ni());
+ setTooltip($(tableSel).find("th:nth-child(5)"), tooltipText_nz());
+ setTooltip($(tableSel).find("th:nth-child(6)"), tooltipText_nc());
+ setTooltip($(tableSel).find("th:nth-child(7)"), tooltipText_mdrse());
+ setTooltip($(tableSel).find("th:nth-child(8)"), tooltipText_rsemd());
+ setTooltip($(tableSel).find("th:nth-child(9)"), tooltipText_qs());
+ setTooltip($(tableSel).find("th:nth-child(10)"), tooltipText_lc());
+ setTooltip($(tableSel).find("th:nth-child(11)"), tooltipText_lcda());
+ setTooltip($(tableSel).find("th:nth-child(12)"), tooltipText_lcms());
+ setTooltip($(tableSel).find("th:nth-child(13)"), tooltipText_lcss());
+ setTooltip($(tableSel).find("th:nth-child(14)"), tooltipText_lcss1());
+ setTooltip($(tableSel).find("th:nth-child(15)"), tooltipText_lcgss());
+ setTooltip($(tableSel).find("th:nth-child(16)"), tooltipText_lclss());
+ setTooltip($(tableSel).find("th:nth-child(17)"), tooltipText_lcds1());
+ setTooltip($(tableSel).find("th:nth-child(18)"), tooltipText_lcds2());
+
+ $(tableSel).bind("sortStart",function() {
+ $("#pbmTable_sortInProgress").show();
+ }).bind("sortEnd",function() {
+ $("#pbmTable_sortInProgress").hide();
+ });
+}
+
+$(document).ready(function() {
+
+ initTablesorter();
+ initTSBMBody();
+
+ initPbmTable("#pbmTable_all");
+ initPbmTable("#pbmTable_lcRegr");
+ initPbmTable("#pbmTable_lcImpr");
+
+ var args = queryStringArgs();
+
+ // Extract database (required):
+ database = extractArg(args, "db");
+ if (database == "") {
+ alert("ERROR: invalid query string (empty database)");
+ return;
+ }
+
+ // Extract context (required):
+ host = extractArg(args, "host");
+ if (host == "") {
+ alert("ERROR: invalid query string (empty host)");
+ return;
+ }
+ platform = extractArg(args, "platform");
+ if (platform == "") {
+ alert("ERROR: invalid query string (empty platform)");
+ return;
+ }
+ branch = extractArg(args, "branch");
+ if (branch == "") {
+ alert("ERROR: invalid query string (empty branch)");
+ return;
+ }
+ var sha11 = extractArg(args, "sha11");
+ if (sha11 == "") {
+ alert("ERROR: invalid query string (empty sha11)");
+ return;
+ }
+ var sha12 = extractArg(args, "sha12");
+ if (sha11 == "") {
+ alert("ERROR: invalid query string (empty sha12)");
+ return;
+ }
+
+ // Extract tolerances and test case filter (optional);
+ var difftol = extractArg(args, "difftol");
+ var durtolmin = extractArg(args, "durtolmin");
+ var durtolmax = extractArg(args, "durtolmax");
+ var testCaseFilter = extractArg(args, "testcasefilter");
+
+ $("#div_tsbm_border").css("display", "none");
+ $("#div_tsbm").css("display", "none");
+ $("#div_perBenchmarkStats").css("display", "none");
+
+ // Fetch per-benchmark stats and populate table:
+ fetchStats(
+ database, host, platform, branch, sha11, sha12, difftol, durtolmin,
+ durtolmax, testCaseFilter);
+});
diff --git a/web/analysis/tsstats.shtml b/web/analysis/tsstats.shtml
new file mode 100644
index 0000000..825d8f5
--- /dev/null
+++ b/web/analysis/tsstats.shtml
@@ -0,0 +1,127 @@
+<html>
+
+<head>
+
+ <title>BM2 - Time Series Statistics</title>
+
+ <script type="text/javascript" src="jquery-1.4.2.min.js"></script>
+ <script type="text/javascript" src="flot/jquery.flot.js"></script>
+ <script type="text/javascript" src="flot/jquery.flot.selection.js"></script>
+ <script type="text/javascript" src="tablesorter/jquery.tablesorter.js">
+ </script>
+ <script type="text/javascript" src="boxover/boxover.js"></script>
+ <script type="text/javascript" src="global.js"></script>
+ <script type="text/javascript" src="tsbmbody.js"></script>
+ <script type="text/javascript" src="tsstats.js"></script>
+
+ <link type="text/css" rel="stylesheet" href="style.css" />
+ <link type="text/css" rel="stylesheet" href="tablesorter/docs/css/jq.css" />
+ <link type="text/css" rel="stylesheet"
+ href="tablesorter/themes/bm/style.css" />
+
+</head>
+
+<body>
+
+<span id="title" style="font-size:18; font-weight:bold">
+BM2 - Time Series Statistics</span>
+&nbsp;&nbsp;&nbsp;
+<span style="white-space:nowrap">
+<span id="status1">no status</span>
+<img alt="spinner1" id="spinner1" src="images/ajax-spinner.gif"
+ style="display:none"/>
+<img alt="nospinner1" id="nospinner1" src="images/nospinner.png"
+ style="display:inline"/>
+</span>
+
+<br />
+
+<a id="mainPageLink" href="">main page</a>
+<script type="text/javascript">
+$("#mainPageLink").attr("href", "http://" + location.host + "/bm2");
+</script>
+
+&nbsp;&nbsp;
+
+<a id="analysisPageLink" href="">analysis page (all types)</a>
+<script type="text/javascript">
+$("#analysisPageLink").attr(
+ "href", "http://" + location.host + "/bm2/analysis");
+</script>
+
+<br />
+<br />
+
+<!-- Overall stats: ... 2 B DONE! -->
+<!--
+<span style="font-size:14; font-weight:bold">Overall statistics:
+<span style="color:red"> (2 B DONE!)</span>
+</span>
+<br />
+<br />
+-->
+
+
+<div id="div_tsbm_border"
+ style="display:none; border-style:solid; border-width:2px; padding:5px">
+<!--#include file="tsbmbody.html" -->
+</div>
+
+
+<div id="div_perBenchmarkStats" style="display:none">
+
+<span id="status2">no status</span>
+<img alt="spinner2" id="spinner2" src="images/ajax-spinner.gif"
+ style="display:none"/>
+<img alt="nospinner2" id="nospinner2" src="images/nospinner.png"
+ style="display:inline"/>
+
+<!-- *** BEGIN Per-benchmark stats *********************************** -->
+<br />
+<!-- <span style="font-size:14; font-weight:bold">Benchmarks:</span> -->
+<select id="select_pbmTable" onchange="selectPbmTable()">
+ <option value="all" selected=1>All benchmarks:</option>
+ <option value="lcRegr">
+ Benchmarks with last change regressing:</option>
+ <option value="lcImpr">
+ Benchmarks with last change improving:</option>
+</select>
+
+<br />
+<span id="pbmTable_nrows">no rows</span>
+<span id="pbmTable_sortInProgress"
+ style="display:none">&nbsp;&nbsp;(sorting ...)</span>
+
+<br />
+<div style="overflow:auto; height:400px; border-style:solid; border-width:1px;">
+
+<div id="div_pbmTable_all" style="display:block">
+<table id="pbmTable_all" class="tablesorter" border="0"
+ cellpadding="0" cellspacing="1">
+<!--#include file="tspbmtabledef.html" -->
+</table>
+</div> <!-- div_pbmTable_all -->
+
+<div id="div_pbmTable_lcRegr" style="display:none">
+<table id="pbmTable_lcRegr" class="tablesorter" border="0"
+ cellpadding="0" cellspacing="1">
+<!--#include file="tspbmtabledef.html" -->
+</table>
+</div> <!-- div_pbmTable_lcRegr -->
+
+<div id="div_pbmTable_lcImpr" style="display:none">
+<table id="pbmTable_lcImpr" class="tablesorter" border="0"
+ cellpadding="0" cellspacing="1">
+<!--#include file="tspbmtabledef.html" -->
+</table>
+</div> <!-- div_pbmTable_lcImpr -->
+
+
+</div>
+<!-- *** END Per-benchmark stats *********************************** -->
+
+</div> <!-- div_perBenchmarkStats -->
+
+</body>
+
+</html>