1<%-- 2 ~ Copyright (c) 2016 Google Inc. All Rights Reserved. 3 ~ 4 ~ Licensed under the Apache License, Version 2.0 (the "License"); you 5 ~ may not use this file except in compliance with the License. You may 6 ~ obtain a copy of the License at 7 ~ 8 ~ http://www.apache.org/licenses/LICENSE-2.0 9 ~ 10 ~ Unless required by applicable law or agreed to in writing, software 11 ~ distributed under the License is distributed on an "AS IS" BASIS, 12 ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 ~ implied. See the License for the specific language governing 14 ~ permissions and limitations under the License. 15 --%> 16<%@ page contentType='text/html;charset=UTF-8' language='java' %> 17<%@ taglib prefix='fn' uri='http://java.sun.com/jsp/jstl/functions' %> 18<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> 19 20<html> 21 <%@ include file="header.jsp" %> 22 <link type='text/css' href='/css/datepicker.css' rel='stylesheet'> 23 <link type='text/css' href='/css/show_graph.css' rel='stylesheet'> 24 <link rel='stylesheet' href='https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.0/jquery-ui.css'> 25 <script type='text/javascript' src='https://www.gstatic.com/charts/loader.js'></script> 26 <script type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js'></script> 27 <body> 28 <script type='text/javascript'> 29 google.charts.load('current', {packages:['corechart', 'table', 'line']}); 30 google.charts.setOnLoadCallback(drawAllGraphs); 31 32 ONE_DAY = 86400000000; 33 MICRO_PER_MILLI = 1000; 34 N_BUCKETS = 200; 35 36 var graphs = ${graphs}; 37 38 $(function() { 39 $('select').material_select(); 40 var date = $('#date').datepicker({ 41 showAnim: 'slideDown', 42 maxDate: new Date() 43 }); 44 date.datepicker('setDate', new Date(${endTime} / MICRO_PER_MILLI)); 45 $('#load').click(load); 46 $('#outlier-select').change(drawAllGraphs); 47 }); 48 49 // Draw all graphs. 50 function drawAllGraphs() { 51 $('#profiling-container').empty(); 52 var percentileIndex = Number($('#outlier-select').val()); 53 54 // Get histogram extrema 55 var histMin = null; 56 var histMax = null; 57 graphs.forEach(function(g) { 58 if (g.type != 'HISTOGRAM') return; 59 var minVal; 60 var maxVal; 61 if (percentileIndex == -1) { 62 minVal = g.min; 63 maxVal = g.max; 64 } else { 65 minVal = g.percentile_values[percentileIndex]; 66 var endIndex = g.percentiles.length - percentileIndex - 1 67 maxVal = g.percentile_values[endIndex]; 68 } 69 if (!histMin || minVal < histMin) histMin = minVal; 70 if (!histMax || maxVal > histMax) histMax = maxVal; 71 }); 72 73 graphs.forEach(function(graph) { 74 if (graph.type == 'LINE_GRAPH') drawLineGraph(graph); 75 else if (graph.type == 'HISTOGRAM') 76 drawHistogram(graph, histMin, histMax); 77 }); 78 } 79 80 /** 81 * Draw a line graph. 82 * 83 * Args: 84 * lineGraph: a JSON object containing the following fields: 85 * - name: the name of the graph 86 * - values: an array of numbers 87 * - ticks: an array of strings to use as x-axis labels 88 * - ids: an array of string labels for each point (e.g. the 89 * build info for the run that produced the point) 90 * - x_label: the string label for the x axis 91 * - y_label: the string label for the y axis 92 */ 93 function drawLineGraph(lineGraph) { 94 if (!lineGraph.ticks || lineGraph.ticks.length < 1) { 95 return; 96 } 97 var title = 'Performance'; 98 if (lineGraph.name) title += ' (' + lineGraph.name + ')'; 99 lineGraph.ticks.forEach(function (label, i) { 100 lineGraph.values[i].unshift(label); 101 }); 102 var data = new google.visualization.DataTable(); 103 data.addColumn('string', lineGraph.x_label); 104 lineGraph.ids.forEach(function(id) { 105 data.addColumn('number', id); 106 }); 107 data.addRows(lineGraph.values); 108 var options = { 109 chart: { 110 title: title, 111 subtitle: lineGraph.y_label 112 }, 113 legend: { position: 'none' } 114 }; 115 var container = $('<div class="row card center-align col s12 graph-wrapper"></div>'); 116 container.appendTo('#profiling-container'); 117 var chartDiv = $('<div class="col s12 graph"></div>'); 118 chartDiv.appendTo(container); 119 var chart = new google.charts.Line(chartDiv[0]); 120 chart.draw(data, options); 121 } 122 123 /** 124 * Draw a histogram. 125 * 126 * Args: 127 * hist: a JSON object containing the following fields: 128 * - name: the name of the graph 129 * - values: an array of numbers 130 * - ids: an array of string labels for each point (e.g. the 131 * build info for the run that produced the point) 132 * - x_label: the string label for the x axis 133 * - y_label: the string label for the y axis 134 * min: the minimum value to display 135 * max: the maximum value to display 136 */ 137 function drawHistogram(hist, min, max) { 138 if (!hist.values || hist.values.length == 0) return; 139 var title = 'Performance'; 140 if (hist.name) title += ' (' + hist.name + ')'; 141 var values = hist.values; 142 var histogramData = values.reduce(function(result, d, i) { 143 if (d <= max && d >= min) result.push([hist.ids[i], d]); 144 return result; 145 }, []); 146 147 var data = google.visualization.arrayToDataTable(histogramData, true); 148 var bucketSize = (max - min) / N_BUCKETS; 149 150 var options = { 151 title: title, 152 titleTextStyle: { 153 color: '#757575', 154 fontSize: 16, 155 bold: false 156 }, 157 legend: { position: 'none' }, 158 colors: ['#4285F4'], 159 fontName: 'Roboto', 160 vAxis:{ 161 title: hist.y_label, 162 titleTextStyle: { 163 color: '#424242', 164 fontSize: 12, 165 italic: false 166 }, 167 textStyle: { 168 fontSize: 12, 169 color: '#757575' 170 }, 171 }, 172 hAxis: { 173 title: hist.x_label, 174 textStyle: { 175 fontSize: 12, 176 color: '#757575' 177 }, 178 titleTextStyle: { 179 color: '#424242', 180 fontSize: 12, 181 italic: false 182 } 183 }, 184 bar: { gap: 0 }, 185 histogram: { 186 minValue: min, 187 maxValue: max, 188 maxNumBuckets: N_BUCKETS, 189 bucketSize: bucketSize 190 }, 191 chartArea: { 192 width: '100%', 193 top: 40, 194 left: 60, 195 height: 375 196 } 197 }; 198 var container = $('<div class="row card col s12 graph-wrapper"></div>'); 199 container.appendTo('#profiling-container'); 200 201 var chartDiv = $('<div class="col s12 graph"></div>'); 202 chartDiv.appendTo(container); 203 var chart = new google.visualization.Histogram(chartDiv[0]); 204 chart.draw(data, options); 205 206 var tableDiv = $('<div class="col s12"></div>'); 207 tableDiv.appendTo(container); 208 209 var tableHtml = '<table class="percentile-table"><thead><tr>'; 210 hist.percentiles.forEach(function(p) { 211 tableHtml += '<th data-field="id">' + p + '%</th>'; 212 }); 213 tableHtml += '</tr></thead><tbody><tr>'; 214 hist.percentile_values.forEach(function(v) { 215 tableHtml += '<td>' + v + '</td>'; 216 }); 217 tableHtml += '</tbody></table>'; 218 $(tableHtml).appendTo(tableDiv); 219 } 220 221 // Reload the page. 222 function load() { 223 var endTime = $('#date').datepicker('getDate').getTime(); 224 endTime = endTime + (ONE_DAY / MICRO_PER_MILLI) - 1; 225 var filterVal = $('#outlier-select').val(); 226 var ctx = '${pageContext.request.contextPath}'; 227 var link = ctx + '/show_graph?profilingPoint=${profilingPointName}' + 228 '&testName=${testName}' + 229 '&endTime=' + (endTime * MICRO_PER_MILLI) + 230 '&filterVal=' + filterVal; 231 if ($('#device-select').prop('selectedIndex') > 1) { 232 link += '&device=' + $('#device-select').val(); 233 } 234 window.open(link,'_self'); 235 } 236 </script> 237 <div id='download' class='fixed-action-btn'> 238 <a id='b' class='btn-floating btn-large red waves-effect waves-light'> 239 <i class='large material-icons'>file_download</i> 240 </a> 241 </div> 242 <div class='container wide'> 243 <div class='row card'> 244 <div id='header-container' class='valign-wrapper col s12'> 245 <div class='col s3 valign'> 246 <h5>Profiling Point:</h5> 247 </div> 248 <div class='col s9 right-align valign'> 249 <h5 class='profiling-name truncate'>${profilingPointName}</h5> 250 </div> 251 </div> 252 <div id='date-container' class='col s12'> 253 <c:set var='offset' value='${showFilterDropdown ? 0 : 2}' /> 254 <c:if test='${showFilterDropdown}'> 255 <div id='outlier-select-wrapper' class='col s2'> 256 <select id='outlier-select'> 257 <option value='-1' ${filterVal eq -1 ? 'selected' : ''}>Show outliers</option> 258 <option value='0' ${filterVal eq 0 ? 'selected' : ''}>Filter outliers (1%)</option> 259 <option value='1' ${filterVal eq 1 ? 'selected' : ''}>Filter outliers (2%)</option> 260 <option value='2' ${filterVal eq 2 ? 'selected' : ''}>Filter outliers (5%)</option> 261 </select> 262 </div> 263 </c:if> 264 <div id='device-select-wrapper' class='input-field col s5 m3 offset-m${offset + 4} offset-s${offset}'> 265 <select id='device-select'> 266 <option value='' disabled>Select device</option> 267 <option value='0' ${empty selectedDevice ? 'selected' : ''}>All Devices</option> 268 <c:forEach items='${devices}' var='device' varStatus='loop'> 269 <option value=${device} ${selectedDevice eq device ? 'selected' : ''}>${device}</option> 270 </c:forEach> 271 </select> 272 </div> 273 <input type='text' id='date' name='date' class='col s4 m2'> 274 <a id='load' class='btn-floating btn-medium red right waves-effect waves-light'> 275 <i class='medium material-icons'>cached</i> 276 </a> 277 </div> 278 </div> 279 <div id='profiling-container'> 280 </div> 281 <c:if test='${not empty error}'> 282 <div id='error-container' class='row card'> 283 <div class='col s10 offset-s1 center-align'> 284 <!-- Error in case of profiling data is missing --> 285 <h5>${error}</h5> 286 </div> 287 </div> 288 </c:if> 289 </div> 290 <%@ include file="footer.jsp" %> 291 </body> 292</html> 293