[LNT] r249772 - Basic graphing support for regression tracking
Chris Matthews via llvm-commits
llvm-commits at lists.llvm.org
Thu Oct 8 16:12:12 PDT 2015
Author: cmatthews
Date: Thu Oct 8 18:12:12 2015
New Revision: 249772
URL: http://llvm.org/viewvc/llvm-project?rev=249772&view=rev
Log:
Basic graphing support for regression tracking
Added:
lnt/trunk/lnt/server/ui/static/lnt_graph.js
Modified:
lnt/trunk/lnt/server/ui/api.py
lnt/trunk/lnt/server/ui/templates/v4_new_regressions.html
Modified: lnt/trunk/lnt/server/ui/api.py
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/ui/api.py?rev=249772&r1=249771&r2=249772&view=diff
==============================================================================
--- lnt/trunk/lnt/server/ui/api.py (original)
+++ lnt/trunk/lnt/server/ui/api.py Thu Oct 8 18:12:12 2015
@@ -3,7 +3,7 @@ from flask import request
from sqlalchemy.orm.exc import NoResultFound
from flask_restful import Resource, reqparse, fields, marshal_with, abort
from lnt.testing import PASS
-
+import json
parser = reqparse.RequestParser()
parser.add_argument('db', type=str)
@@ -155,21 +155,10 @@ class Order(Resource):
return changes
-graph_fields = {
- 'val': fields.Float,
- 'rev': fields.String,
- 'time': fields.DateTime(dt_format=DATE_FORMAT),
- }
-graph_list_fields = {
- fields.List(fields.Nested(graph_fields)),
-}
-
-
class Graph(Resource):
"""List all the machines and give summary information."""
method_decorators = [in_db]
- @marshal_with(graph_fields)
def get(self, machine_id, test_id, field_index):
"""Get the data for a particular line in a graph."""
ts = request.get_testsuite()
@@ -190,16 +179,14 @@ class Graph(Resource):
.join(ts.Order) \
.filter(ts.Run.machine_id == machine.id) \
.filter(ts.Sample.test == test) \
- .filter(field.column != None)
+ .filter(field.column != None) \
+ .order_by(ts.Order.llvm_project_revision)
if field.status_field:
q = q.filter((field.status_field.column == PASS) |
(field.status_field.column == None))
+ samples = [[int(rev), val, {'label': rev, 'date': str(time)}] for val, rev, time in q.all()]
- samples = [{'val': val, 'rev': rev, 'time': time} for val, rev, time in q.all()]
- import pprint
- print "Samples"
- pprint.pprint(samples)
return samples
Added: lnt/trunk/lnt/server/ui/static/lnt_graph.js
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/ui/static/lnt_graph.js?rev=249772&view=auto
==============================================================================
--- lnt/trunk/lnt/server/ui/static/lnt_graph.js (added)
+++ lnt/trunk/lnt/server/ui/static/lnt_graph.js Thu Oct 8 18:12:12 2015
@@ -0,0 +1,101 @@
+// Keep the graph data we download.
+// Each element is a list of graph data points.
+var data_cache = [];
+var is_checked = []; // The current list of lines to plot.
+var normalize = false;
+
+
+function try_normal(data_array, end_rev) {
+ $("#graph_range").prop("min", 0);
+ var max = $("#graph_range").prop("max");
+ if (max < data_array.length) {
+ $("#graph_range").prop("max", data_array.length);
+ }
+ var center = -1;
+ for (var i = 0; i < data_array.length; i++) {
+ if (data_array[i][0] == end_rev) {
+ center = i;
+ break;
+ }
+ }
+ console.assert(center != -1, "Center was not found");
+ var smaller = $("#graph_range").val();
+ var total = data_array.length;
+ var to_draw = total - smaller;
+ var upper = data_array.length;
+ var lower = 0;
+ if (center - (to_draw/2) > 0) {
+ lower = center - (to_draw/2);
+ }
+ if (center + (to_draw/2) < total) {
+ upper = center + (to_draw/2);
+ }
+
+ data_array = data_array.slice(lower, upper);
+
+ if (normalize) {
+ console.log(data_array);
+ return normalize_data(data_array);
+ } else {
+ return data_array;
+ }
+}
+
+function normalize_data(data_array) {
+ var new_data = []
+
+
+ for (var i = 0; i < data_array.length; i++) {
+ new_data[i] = jQuery.extend({}, data_array[i]);
+ new_data[i][1] = data_array[i][1] / data_array[0][1];
+ }
+ return new_data;
+
+}
+
+function make_graph_point_entry(data, color) {
+ var entry = {"color": color,
+ "data": data,
+ "lines": {"show": false},
+ "points": {"fill": true,
+ "radius": 0.25,
+ "show": true
+ }
+ };
+ return entry;
+}
+
+var color_codes = ["#4D4D4D",
+ "#5DA5DA",
+ "#FAA43A",
+ "#60BD68",
+ "#F17CB0",
+ "#B2912F",
+ "#B276B2",
+ "#DECF3F",
+ "#F15854"]
+
+function new_graph_data_callback(data, index) {
+ data_cache[index] = data;
+ is_checked[index] = true;
+ update_graph();
+}
+
+function update_graph() {
+ var to_draw = [];
+ var starts = [];
+ var ends = [];
+ for ( var i = 0; i < changes.length; i++) {
+ if (is_checked[i]) {
+ starts.push(changes[i].start);
+ ends.push(changes[i].end);
+ var color = color_codes[i % color_codes.length];
+ var data = try_normal(data_cache[i], changes[i].end);
+ to_draw.push(make_graph_point_entry(data, color));
+ to_draw.push({"color": color, "data": data});
+ }
+ }
+ var lowest_rev = Math.min.apply(Math, starts);
+ var highest_rev = Math.max.apply(Math, ends);
+ init(to_draw, lowest_rev, highest_rev);
+}
Modified: lnt/trunk/lnt/server/ui/templates/v4_new_regressions.html
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/ui/templates/v4_new_regressions.html?rev=249772&r1=249771&r2=249772&view=diff
==============================================================================
--- lnt/trunk/lnt/server/ui/templates/v4_new_regressions.html (original)
+++ lnt/trunk/lnt/server/ui/templates/v4_new_regressions.html Thu Oct 8 18:12:12 2015
@@ -7,11 +7,200 @@
{% set components = [(testsuite_name, v4_url_for("v4_recent_activity"))] %}
{% block title %}Regression Triage{% endblock %}
+{% block head %}
+ <script src="{{ url_for('.static', filename='popup.js') }}"></script>
+ <script src="{{ url_for('.static', filename='sorttable.js') }}"></script>
+ <script language="javascript" type="text/javascript"
+ src="{{ url_for('.static',
+ filename='flot/jquery.flot.min.js') }}"> </script>
+ <script language="javascript" type="text/javascript"
+ src="{{ url_for('.static',
+ filename='flot/jquery.flot.errorbars.min.js') }}"> </script>
+ <script language="javascript" type="text/javascript"
+ src="{{ url_for('.static',
+ filename='flot/jquery.flot.navigate.min.js') }}"> </script>
+ <script language="javascript" type="text/javascript"
+ src="{{ url_for('.static',
+ filename='flot/jquery.flot.selection.min.js') }}"> </script>
+ <script language="javascript" type="text/javascript"
+ src="{{ url_for('.static',
+ filename='flot/jquery.flot.highlight.min.js') }}"></script>
+ <script src="{{ url_for('.static', filename='lnt_graph.js') }}"></script>
+{% endblock %}
+
+{% block javascript %}
+var g = {}
+{% set api_graph = "api/db_default/v4/" + testsuite_name + "/graph"%}
+var changes = [
+{% for form_change in form.field_changes%}
+ {% set fc = changes[loop.index -1] %}
+ {"url": "/{{api_graph}}/{{ fc.ri.machine.id}}/{{fc.ri.test.id}}/{{fc.ri.field.index}}",
+ "start": {{fc.ri.start_order.llvm_project_revision}},
+ "end": {{fc.ri.end_order.llvm_project_revision}}
+ },
+{% endfor %}
+];
+
+/* Bind events to the zoom bar buttons, so that
+ * the zoom buttons work, then position them
+ * over top of the main graph.
+ */
+function bind_zoom_bar(my_plot) {
+
+ $('#out').click(function (e) {
+ e.preventDefault();
+ my_plot.zoomOut();
+ });
+
+ $('#in').click(function (e) {
+ e.preventDefault();
+ my_plot.zoom();
+ });
+
+
+ // Now move the bottons onto the graph.
+ $('#zoombar').css('position', 'relative');
+ $('#zoombar').css('left', '40px');
+ $('#zoombar').css('top', '-235px');
+}
+$('#normalize').click(function (e) {
+ normalize = !normalize;
+ alert(normalize);
+ update_graph();
+});
+function init(data, start_highlight, end_highlight) {
+ // Set up the primary graph.
+ var graph = $("#graph");
+ var graph_plots = data;
+ var line_width = 1;
+ if (data.length > 0 && data[0]['data'].length < 50) {
+ line_width = 2;
+ }
+ var graph_options = {
+ series : {
+ lines : {
+ lineWidth : line_width },
+ shadowSize : 0
+ },
+ highlight : {
+
+ range: {"end": [end_highlight], "start": [start_highlight]},
+ alpha: "0.1",
+ stroke: false,
+
+ },
+ zoom : { interactive : false },
+ pan : { interactive : true,
+ frameRate: 60 },
+ grid : {
+ hoverable : true }
+ };
+
+ var main_plot = $.plot("#graph", graph_plots, graph_options);
+
+ // Add tooltips.
+ $("#graph").bind("plothover", function(e,p,i) {
+ update_tooltip(e, p, i, show_tooltip, graph_plots); });
+ bind_zoom_bar(main_plot);
+
+
+}
+
+// Show our overlay tooltip.
+g.current_tip_point = null;
+function show_tooltip(x, y, item, pos, graph_data) {
+
+ // Given the event handler item, get the graph metadata.
+ function extract_metadata(item, graph_data) {
+ var index = item.dataIndex;
+ var series_index = item.seriesIndex;
+ // Graph data is formatted as [x, y, meta_data].
+ var meta_data = item.series.data[series_index][2];
+ return meta_data;
+
+ }
+ var data = item.datapoint;
+ var meta_data = extract_metadata(item, graph_data);
+ var tip_body = '<div id="tooltip">';
+
+ if ("test_name" in meta_data) {
+ tip_body += "<b>Test:</b> " + meta_data.test_name + "<br>";
+ }
+
+ if ("label" in meta_data) {
+ tip_body += "<b>Revision:</b> " + meta_data.label + "<br>";
+ }
+ tip_body += "<b>Value:</b> " + data[1].toFixed(4) + "<br>";
+
+ if ("date" in meta_data) {
+ tip_body += "<b>Date:</b> " + meta_data.date;
+ }
+ tip_body += "</div>";
+ var tooltip_div = $(tip_body).css( {
+ position: 'absolute',
+ display: 'none',
+ top: y + 5,
+ left: x + 5,
+ border: '1px solid #fdd',
+ padding: '2px',
+ 'background-color': '#fee',
+ opacity: 0.80
+ }).appendTo("body").fadeIn(200);
+
+ // Now make sure the tool tip is on the graph canvas.
+ var tt_position = tooltip_div.position();
+ var tt_offset = tooltip_div.offset();
+
+ var graph_div = $("#graph");
+ var graph_position = graph_div.position();
+
+ // The right edge of the graph.
+ var max_width = graph_position.left + graph_div.width();
+ // The right edge of the tool tip.
+ var tt_right = tt_position.left + tooltip_div.width();
+
+ if (tt_right > max_width) {
+ var diff = tt_right - max_width
+ var GRAPH_BORDER = 10;
+ var VISUAL_APPEAL = 10;
+ tooltip_div.css({'left' : tt_position.left - diff
+ - GRAPH_BORDER - VISUAL_APPEAL});
+ }
+
+}
+
+// Event handler function to update the tooltop.
+function update_tooltip(event, pos, item, show_fn, graph_data) {
+ if (!item) {
+ $("#tooltip").remove();
+ g.current_tip_point = null;
+ return;
+ }
+
+ if (!g.current_tip_point || (g.current_tip_point[0] != item.datapoint[0] ||
+ g.current_tip_point[1] != item.datapoint[1])) {
+ $("#tooltip").remove();
+ g.current_tip_point = item.datapoint;
+ show_fn(pos.pageX, pos.pageY, item, pos, graph_data);
+ }
+}
+{% endblock %}
+
{% block body %}
<section id="Changes" />
<h3>Recent Changes</h3>
+<table><td><tr>Data: More </tr><tr><input type="range" id="graph_range" value="1" onchange="update_graph()"></tr><tr> Less</tr></td></table>
+<div id="graph" style="height:250px"></div>
+<div id="zoombar" style="width:40px;z-index: 999;">
+ <button id="in" type="button" class="btn btn-default" style="width:100%;text-align:center;">+</button>
+ <br>
+ <button id="out" type="button" class="btn btn-default" style="width:100%; text-align:center;">-</button>
+ <br>
+ <button id="normalize" type="button" class="btn btn-default" style="width:100%; text-align:center;">%</button>
+</div>
+
<form method="POST" action="{{ v4_url_for("v4_new_regressions") }}">
{{ form.hidden_tag() }}
@@ -20,7 +209,7 @@
<thead>
<tr>
- <th>X</th>
+ <th><button id="clear" type="button" class="btn btn-default" style="width:100%;text-align:center;" onclick="clear_checks()">X</button></th>
<th>Machine</th>
<th>Metric</th>
<th>Test</th>
@@ -39,7 +228,13 @@
{% for form_change in form.field_changes%}
{% set fc = changes[loop.index -1] %}
<tr>
- <td>{{ form_change }}</td>
+ <td>
+
+ <table>
+ <tr style="background:transparent;">
+ <td style="background:transparent;"></td><td>{{ form_change }}</td>
+ </tr>
+ </table>
<td>{{utils.render_machine(fc.ri.machine)}}</td>
<td> {{ fc.ri.field.name }} </td>
{% set graph_base=v4_url_for('v4_graph', highlight_run=fc.run.id) %}
@@ -64,12 +259,57 @@
<script type="text/javascript">
+
+function clear_checks() {
+ $('input:checkbox').removeAttr('checked');
+ $('input:checkbox').trigger('change');
+}
+
+function register_checkboxes() {
+ $(':checkbox').change(function(){
+ var c = this.checked
+ var id = this.id;
+ var index = id.split("-")[1];
+
+ if (c) {
+ var color = color_codes[index % color_codes.length];
+ var prev_cell = $(this).closest('td').prev();
+ prev_cell.css("background-color", color);
+ $.getJSON(changes[index]["url"], function(data) {
+ new_graph_data_callback(data, index);
+ });
+ } else {
+ is_checked[index] = false;
+ var prev_cell = $(this).closest('td').prev();
+ prev_cell.css("background-color", "transparent");
+ }
+ update_graph();
+ });
+ }
+
$(document).ready( function () {
$('#changes_table').DataTable({
- "sDom": '<"top"if>rt<"bottom"Flp>'
+ "sDom": '<"top"if>rt<"bottom"Flp>',
+ "drawCallback": function( settings ) {
+ register_checkboxes();
+ }
});
+
+ for (var i = 0; i < changes.length; i++) {
+ is_checked[i] = false;
+ }
+
+ register_checkboxes();
+ $('#normalize').click(function (e) {
+ normalize = !normalize;
+ update_graph();
+ });
+ update_graph();
+
+
} );
</script>
+
{% endblock %}
More information about the llvm-commits
mailing list