diff options
Diffstat (limited to 'web/analysis')
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 += " "; + 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> + <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 ≥ 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 /> + + <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 /> + <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 /> + + <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 /> + + <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 /> +<subname 1><b>:</b><subname 2><b>(</b><subname 3><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> + <img src="images/help_plot_overview_anno.png" /><br /><br /> + + +<a name="changes" /> +<h3>Significant changes</h3> + <img src="images/help_plot_changes_anno.png" /><br /><br /> + + +<h3>Sample size</h3> + <img src="images/help_plot_samplesize_anno.png" /><br /><br /> + + +<h3>Non-positive observations</h3> + <img src="images/help_plot_nonposobs_anno.png" /><br /><br /> + + +<h3>Invalid observations</h3> + <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). + <img src="images/help_plot_rse_anno.png" /><br /><br /> + + +<h3>Missing data</h3> + <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 /> + <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 Binary files differnew file mode 100644 index 0000000..d0bce15 --- /dev/null +++ b/web/analysis/images/ajax-spinner.gif diff --git a/web/analysis/images/bg_benchmark.png b/web/analysis/images/bg_benchmark.png Binary files differnew file mode 100644 index 0000000..5fc3d66 --- /dev/null +++ b/web/analysis/images/bg_benchmark.png diff --git a/web/analysis/images/bg_context1.png b/web/analysis/images/bg_context1.png Binary files differnew file mode 100644 index 0000000..b8bafa3 --- /dev/null +++ b/web/analysis/images/bg_context1.png diff --git a/web/analysis/images/bg_context2.png b/web/analysis/images/bg_context2.png Binary files differnew file mode 100644 index 0000000..4dc7c87 --- /dev/null +++ b/web/analysis/images/bg_context2.png diff --git a/web/analysis/images/bg_metric.png b/web/analysis/images/bg_metric.png Binary files differnew file mode 100644 index 0000000..0d4c135 --- /dev/null +++ b/web/analysis/images/bg_metric.png diff --git a/web/analysis/images/canvas-bg.png b/web/analysis/images/canvas-bg.png Binary files differnew file mode 100644 index 0000000..93701d4 --- /dev/null +++ b/web/analysis/images/canvas-bg.png diff --git a/web/analysis/images/help_overview.odp b/web/analysis/images/help_overview.odp Binary files differnew file mode 100644 index 0000000..7d85b7b --- /dev/null +++ b/web/analysis/images/help_overview.odp diff --git a/web/analysis/images/help_overview.png b/web/analysis/images/help_overview.png Binary files differnew file mode 100644 index 0000000..a184b06 --- /dev/null +++ b/web/analysis/images/help_overview.png diff --git a/web/analysis/images/help_overview_anno.png b/web/analysis/images/help_overview_anno.png Binary files differnew file mode 100644 index 0000000..8df94a1 --- /dev/null +++ b/web/analysis/images/help_overview_anno.png diff --git a/web/analysis/images/help_plot_changes.odp b/web/analysis/images/help_plot_changes.odp Binary files differnew file mode 100644 index 0000000..dc60331 --- /dev/null +++ b/web/analysis/images/help_plot_changes.odp diff --git a/web/analysis/images/help_plot_changes.png b/web/analysis/images/help_plot_changes.png Binary files differnew file mode 100644 index 0000000..08817e9 --- /dev/null +++ b/web/analysis/images/help_plot_changes.png diff --git a/web/analysis/images/help_plot_changes_anno.png b/web/analysis/images/help_plot_changes_anno.png Binary files differnew file mode 100644 index 0000000..791db6a --- /dev/null +++ b/web/analysis/images/help_plot_changes_anno.png diff --git a/web/analysis/images/help_plot_invalidobs.odp b/web/analysis/images/help_plot_invalidobs.odp Binary files differnew file mode 100644 index 0000000..1c3e957 --- /dev/null +++ b/web/analysis/images/help_plot_invalidobs.odp diff --git a/web/analysis/images/help_plot_invalidobs.png b/web/analysis/images/help_plot_invalidobs.png Binary files differnew file mode 100644 index 0000000..74f80de --- /dev/null +++ b/web/analysis/images/help_plot_invalidobs.png diff --git a/web/analysis/images/help_plot_invalidobs_anno.png b/web/analysis/images/help_plot_invalidobs_anno.png Binary files differnew file mode 100644 index 0000000..31ddecb --- /dev/null +++ b/web/analysis/images/help_plot_invalidobs_anno.png diff --git a/web/analysis/images/help_plot_missing.odp b/web/analysis/images/help_plot_missing.odp Binary files differnew file mode 100644 index 0000000..61d6a98 --- /dev/null +++ b/web/analysis/images/help_plot_missing.odp diff --git a/web/analysis/images/help_plot_missing.png b/web/analysis/images/help_plot_missing.png Binary files differnew file mode 100644 index 0000000..7165797 --- /dev/null +++ b/web/analysis/images/help_plot_missing.png diff --git a/web/analysis/images/help_plot_missing_anno.png b/web/analysis/images/help_plot_missing_anno.png Binary files differnew file mode 100644 index 0000000..00359d7 --- /dev/null +++ b/web/analysis/images/help_plot_missing_anno.png diff --git a/web/analysis/images/help_plot_nonposobs.odp b/web/analysis/images/help_plot_nonposobs.odp Binary files differnew file mode 100644 index 0000000..fa50e67 --- /dev/null +++ b/web/analysis/images/help_plot_nonposobs.odp diff --git a/web/analysis/images/help_plot_nonposobs.png b/web/analysis/images/help_plot_nonposobs.png Binary files differnew file mode 100644 index 0000000..4805862 --- /dev/null +++ b/web/analysis/images/help_plot_nonposobs.png diff --git a/web/analysis/images/help_plot_nonposobs_anno.png b/web/analysis/images/help_plot_nonposobs_anno.png Binary files differnew file mode 100644 index 0000000..980dd46 --- /dev/null +++ b/web/analysis/images/help_plot_nonposobs_anno.png diff --git a/web/analysis/images/help_plot_overview.odp b/web/analysis/images/help_plot_overview.odp Binary files differnew file mode 100644 index 0000000..8204ee5 --- /dev/null +++ b/web/analysis/images/help_plot_overview.odp diff --git a/web/analysis/images/help_plot_overview.png b/web/analysis/images/help_plot_overview.png Binary files differnew file mode 100644 index 0000000..c2a1832 --- /dev/null +++ b/web/analysis/images/help_plot_overview.png diff --git a/web/analysis/images/help_plot_overview_anno.png b/web/analysis/images/help_plot_overview_anno.png Binary files differnew file mode 100644 index 0000000..3affed7 --- /dev/null +++ b/web/analysis/images/help_plot_overview_anno.png diff --git a/web/analysis/images/help_plot_rse.odp b/web/analysis/images/help_plot_rse.odp Binary files differnew file mode 100644 index 0000000..03e44f4 --- /dev/null +++ b/web/analysis/images/help_plot_rse.odp diff --git a/web/analysis/images/help_plot_rse.png b/web/analysis/images/help_plot_rse.png Binary files differnew file mode 100644 index 0000000..b04fa9b --- /dev/null +++ b/web/analysis/images/help_plot_rse.png diff --git a/web/analysis/images/help_plot_rse_anno.png b/web/analysis/images/help_plot_rse_anno.png Binary files differnew file mode 100644 index 0000000..555e6f3 --- /dev/null +++ b/web/analysis/images/help_plot_rse_anno.png diff --git a/web/analysis/images/help_plot_samplesize.odp b/web/analysis/images/help_plot_samplesize.odp Binary files differnew file mode 100644 index 0000000..cd73075 --- /dev/null +++ b/web/analysis/images/help_plot_samplesize.odp diff --git a/web/analysis/images/help_plot_samplesize.png b/web/analysis/images/help_plot_samplesize.png Binary files differnew file mode 100644 index 0000000..325f571 --- /dev/null +++ b/web/analysis/images/help_plot_samplesize.png diff --git a/web/analysis/images/help_plot_samplesize_anno.png b/web/analysis/images/help_plot_samplesize_anno.png Binary files differnew file mode 100644 index 0000000..6082b59 --- /dev/null +++ b/web/analysis/images/help_plot_samplesize_anno.png diff --git a/web/analysis/images/help_plot_selected.odp b/web/analysis/images/help_plot_selected.odp Binary files differnew file mode 100644 index 0000000..77a3f79 --- /dev/null +++ b/web/analysis/images/help_plot_selected.odp diff --git a/web/analysis/images/help_plot_selected.png b/web/analysis/images/help_plot_selected.png Binary files differnew file mode 100644 index 0000000..4f52576 --- /dev/null +++ b/web/analysis/images/help_plot_selected.png diff --git a/web/analysis/images/help_plot_selected_anno.png b/web/analysis/images/help_plot_selected_anno.png Binary files differnew file mode 100644 index 0000000..d3a1a08 --- /dev/null +++ b/web/analysis/images/help_plot_selected_anno.png diff --git a/web/analysis/images/lcms.odp b/web/analysis/images/lcms.odp Binary files differnew file mode 100644 index 0000000..6b79925 --- /dev/null +++ b/web/analysis/images/lcms.odp diff --git a/web/analysis/images/lcms.png b/web/analysis/images/lcms.png Binary files differnew file mode 100644 index 0000000..0bcd6ad --- /dev/null +++ b/web/analysis/images/lcms.png diff --git a/web/analysis/images/nospinner.png b/web/analysis/images/nospinner.png Binary files differnew file mode 100644 index 0000000..4f95459 --- /dev/null +++ b/web/analysis/images/nospinner.png diff --git a/web/analysis/images/rse.odp b/web/analysis/images/rse.odp Binary files differnew file mode 100644 index 0000000..9effed8 --- /dev/null +++ b/web/analysis/images/rse.odp diff --git a/web/analysis/images/rse.png b/web/analysis/images/rse.png Binary files differnew file mode 100644 index 0000000..b75661b --- /dev/null +++ b/web/analysis/images/rse.png 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> + + +<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"> + +<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"> + +<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"> + 1 </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"> + 1 </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"> 2 </span> to Context + <span class="context1"> 1 </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"> + 1 </span> to Context <span class="context2"> + 2 </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> 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 / <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> + +<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 " : " ") + + sha1 + " (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) + + "¬e=" + 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> + +<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> + + + +<a id="analysisPageLink" href="">analysis page (all types)</a> +<script type="text/javascript"> +$("#analysisPageLink").attr( + "href", "http://" + location.host + "/bm2/analysis"); +</script> + + + +<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> + +<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> + + + +<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"> (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> + +<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> + + + +<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 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 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>> 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>< 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"> (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 1</th> + <script type="text/javascript"> + setTooltip($("#median1"), "Median observation in Context 1"); + </script> + <th id="median2">Median 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 Binary files differnew file mode 100644 index 0000000..1ebe2cf --- /dev/null +++ b/web/analysis/tablesorter/themes/bm/asc.png diff --git a/web/analysis/tablesorter/themes/bm/bg.png b/web/analysis/tablesorter/themes/bm/bg.png Binary files differnew file mode 100644 index 0000000..b7be99f --- /dev/null +++ b/web/analysis/tablesorter/themes/bm/bg.png diff --git a/web/analysis/tablesorter/themes/bm/desc.png b/web/analysis/tablesorter/themes/bm/desc.png Binary files differnew file mode 100644 index 0000000..5f445f8 --- /dev/null +++ b/web/analysis/tablesorter/themes/bm/desc.png 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> + +<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 ≥ 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 />" + + " " + + "<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 />" + + " " + + "<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 />" + + " " + + "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 />" + + " " + + "<img src=\"images/lcms.png\" />"; +} + +function tooltipText_lcss() { + return "Stability score for the last significant change:<br /><br />" + + " " + + "(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 />" + + " " + + "(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, " ")); + $("#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) + " (" + 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 + " (" + 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, " ") + "</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> + +<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> + + + +<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"> (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> |