Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(251)

Side by Side Diff: master/public_html/buildsteps.html

Issue 648353002: Remove Skia's forked buildbot code (Closed) Base URL: https://skia.googlesource.com/buildbot.git@master
Patch Set: Address comment Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « master/public_html/buildslave_idle.html ('k') | master/public_html/default.css » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 <html>
2 <head>
3 <title>Skia Buildstep Dashboard</title>
4 <link rel="icon" href="favicon.ico">
5 <script language="JavaScript">
6 "use strict";
7
8 var DEFAULT_MASTER_FILTER = "*";
9 var DEFAULT_BUILDER_FILTER = "*";
10 var DEFAULT_BUILDSTEP_FILTER = "*";
11 var DEFAULT_TIME_PERIOD = 24*60*60; // 24 hours in seconds.
12
13 // Column indices for grouping Graphite data.
14 var GROUP_NODE_BUILDER = 2;
15 var GROUP_NODE_BUILDSTEP = 3;
16
17 // We hard-code this giant interval to be passed to Graphite's summarize
18 // function to ensure that the data ends up grouped into one big bucket, no
19 // matter how we set the time period.
20 var SUMMARIZE_INTERVAL = "10year";
21
22 // Timestamp of when we started loading data. Used to compute load time.
23 var loadStart = null;
24
25 // Used to store information about what data is currently being loaded.
26 var currentlyLoadingData = null;
27
28 // Metrics to load from Graphite.
29 // Takes the form: [[metric_name, merge_function], ...]
30 var metrics = [["success", "sum"],
31 ["failure", "sum"],
32 ["duration", "avg"]];
33
34 // Metrics to derive from the loaded metrics above. We display these in the
35 // table. Takes the form: [[metric_name, derivation_fn], ...], where
36 // derivation_fn is a function which takes as a parameter a dictionary
37 // containing values for all of the above metrics for a given buildStep and
38 // returns a value, which may be null.
39 var derivedMetrics = [
40 ["Duration", function(stepData) {
41 if (stepData["duration"] == undefined) {
42 return null;
43 }
44 return stepData["duration"];
45 }],
46 ["Failure Rate", function(stepData) {
47 if (stepData["success"] == undefined ||
48 stepData["failure"] == undefined) {
49 return null;
50 }
51 var success = parseFloat(stepData["success"]);
52 var failure = parseFloat(stepData["failure"]);
53 var totalRuns = success + failure;
54 var failureRate = failure / totalRuns;
55 if (totalRuns <= 0) {
56 return null;
57 }
58 return failureRate;
59 }],
60 ["Total Runs", function(stepData) {
61 if (stepData["success"] == undefined ||
62 stepData["failure"] == undefined) {
63 return null;
64 }
65 var success = parseFloat(stepData["success"]);
66 var failure = parseFloat(stepData["failure"]);
67 return success + failure;
68 }],
69 ];
70
71 // Use the second data column as the default sort index, since that typically
72 // indicates the most important column which isn't a label.
73 var defaultSortIndex = 1;
74
75 /**
76 * Display text or HTML in the logging div.
77 *
78 * @param {string} msg The HTML or text to display.
79 */
80 function setMessage(msg) {
81 console.log(msg);
82 document.getElementById("logging_div").innerHTML = msg;
83 }
84
85 /**
86 * Function to call when starting to load data. Prints the given URL and
87 * stores some data for retrieval on loadingDone().
88 *
89 * @param {string} url The URL that we're loading.
90 * @param {Object} data Arbitrary data to store.s
91 */
92 function loadingStart(url, data) {
93 document.body.style.cursor = "wait";
94 document.getElementById("buildstep_div").style.display = "none";
95 document.getElementById("builder_div").style.display = "none";
96 document.getElementById("load_data_button").disabled = true;
97 loadStart = new Date().getTime();
98 setMessage("Loading data from " + url);
99 currentlyLoadingData = data;
100 }
101
102 /**
103 * Function to call when done loading data. Returns any data stored on
104 * loadingStart().
105 */
106 function loadingDone() {
107 document.body.style.cursor = "auto";
108 document.getElementById("buildstep_div").style.display = "block";
109 document.getElementById("builder_div").style.display = "block";
110 document.getElementById("load_data_button").disabled = false;
111 var message = "";
112 if (loadStart) {
113 var elapsedSeconds = (new Date().getTime() - loadStart) / 1000;
114 message = "Loaded data in " + elapsedSeconds + " seconds.";
115 }
116 setMessage(message);
117 var retData = currentlyLoadingData;
118 currentlyLoadingData = null;
119 return retData;
120 }
121
122 /**
123 * Load data from the given URL using JSONP.
124 *
125 * @param {string} url The URL from which to load data.
126 * @param {string} callbackName The name of the function to use as a callback.
127 */
128 function loadJSONP(url, callbackName) {
129 var script = document.createElement("script");
130 var join = "&";
131 if (url.indexOf("?") < 0) {
132 join = "?";
133 }
134 script.src = url + join + "jsonp=" + callbackName;
135 document.head.appendChild(script);
136 }
137
138 /**
139 * Re-organize Graphite data into a dictionary.
140 *
141 * @param {Array<Object>} data Data to organize.
142 */
143 function graphiteDataDict(data) {
144 var dataDict = {};
145 for (var i = 0; i < data.length; ++i) {
146 var splitName = data[i]["target"].split(".");
147 var name = splitName.slice(0, splitName.length - 1).join(".");
148 var resultType = splitName[1];
149 var datapoints = data[i]["datapoints"];
150 var result = 0;
151 if (datapoints.length > 0) {
152 result = datapoints[datapoints.length - 1][0];
153 }
154 if (result == null) {
155 result = 0;
156 }
157 if (!dataDict[name]) {
158 dataDict[name] = {};
159 }
160 dataDict[name][resultType] = result;
161 dataDict[name]["name"] = name;
162 }
163 return dataDict;
164 }
165
166 /**
167 * Create lists of column names and rows given a data dictionary.
168 *
169 * @param {Object} dataDict data to organize into columns and rows.
170 */
171 function getColsAndRows(dataDict) {
172 // Create the set of columns
173 var cols = ["Name"];
174 for (var i = 0; i < derivedMetrics.length; ++i) {
175 cols.push(derivedMetrics[i][0]);
176 }
177
178 // Create a row of data for each buildStep.
179 var rows = [];
180 for (var name in dataDict) {
181 var row = [name];
182 // Fill in the data row.
183 for (var i = 0; i < derivedMetrics.length; ++i) {
184 var value = derivedMetrics[i][1](dataDict[name]);
185 row.push(value);
186 }
187 rows.push(row);
188 }
189 return [cols, rows]
190 }
191
192 /**
193 * Build the data table given a set of data.
194 *
195 * @param {Array<Object>} data The data to put into the table.
196 * @param {string} title The title of the table.
197 * @param {string} containerId ID of the container to hold the table.
198 */
199 function makeTableFromData(data, title, containerId) {
200 var buildStepData = graphiteDataDict(data);
201 var colsAndRows = getColsAndRows(buildStepData);
202 rebuildTable(title, colsAndRows[0], colsAndRows[1], containerId,
203 null, true);
204 }
205
206 /**
207 * Sort the rows on the given column index.
208 *
209 * @param {Array<Array>} rows The rows to sort.
210 * @param {number} sortIndex The index of the column by which to sort.
211 * @param {number} sortOrder 1 or -1; determines whether to sort ascending or
212 * descending.
213 */
214 function sortRows(rows, sortIndex, sortOrder) {
215 // Sort the table rows by sortIndex.
216 if (null == sortIndex || undefined == sortIndex) {
217 sortIndex = defaultSortIndex;
218 }
219 rows.sort(function(a, b) {
220 if (null == a) { return -sortOrder; }
221 if (null == b) { return sortOrder; }
222 if (null == a[sortIndex]) { return -sortOrder; }
223 if (null == b[sortIndex]) { return sortOrder; }
224 if (a[sortIndex] > b[sortIndex]) { return sortOrder; }
225 if (a[sortIndex] < b[sortIndex]) { return -sortOrder; }
226 return 0;
227 });
228 }
229
230 /**
231 * Rebuild the data table.
232 *
233 * @param {string} title The title of the table.
234 * @param {Array<string>} cols Array of column names.
235 * @param {Array<Array>} rows Array of row data.
236 * @param {string} containerId ID of the container where the table goes.
237 * @param {number} sortIndex The index of the column by which to sort.
238 * @param {boolean} reload Whether or not we reloaded data. This affects the
239 * sorting of rows.
240 */
241 function rebuildTable(title, cols, rows, containerId, sortIndex, reload) {
242 var tableId = containerId + "_table";
243 var oldTable = document.getElementById(tableId);
244 var lastSortIndex = null;
245 var sortOrder = -1;
246 if (oldTable) {
247 lastSortIndex = oldTable.sortIndex;
248 if (reload) {
249 sortIndex = lastSortIndex;
250 } else if (sortIndex == lastSortIndex) {
251 sortOrder = -oldTable.sortOrder;
252 }
253 }
254 sortRows(rows, sortIndex, sortOrder);
255
256 var table = document.createElement("table");
257 table.sortIndex = sortIndex;
258 table.sortOrder = sortOrder;
259 var thead = document.createElement("thead");
260
261 for (var i = 0; i < cols.length; ++i) {
262 var th = document.createElement("th");
263 th.style.padding = "5px";
264 th.style.textAlign = "right";
265 var sortLink = document.createElement("a");
266 sortLink.href = "#";
267 sortLink.innerHTML = cols[i];
268 sortLink.tableTitle = title;
269 sortLink.containerId = containerId;
270 sortLink.sortIndex = i;
271 sortLink.rowsObj = rows;
272 sortLink.colsObj = cols;
273 sortLink.addEventListener("click", function(event) {
274 var cols = event.target.colsObj;
275 var rows = event.target.rowsObj;
276 var containerId = event.target.containerId;
277 var sortIndex = event.target.sortIndex;
278 var tableTitle = event.target.tableTitle;
279 rebuildTable(tableTitle, cols, rows, containerId, sortIndex, false);
280 });
281 th.appendChild(sortLink);
282 thead.appendChild(th);
283 }
284
285 table.appendChild(thead);
286
287 for (var i = 0; i < rows.length; ++i) {
288 var tr = document.createElement("tr");
289 for (var j = 0; j < rows[i].length; ++j) {
290 var td = document.createElement("td");
291 td.style.padding = "5px";
292 td.style.textAlign = "right";
293 var value = rows[i][j];
294 if (typeof value == "number") {
295 value = parseFloat(value).toFixed(2);
296 }
297 // Add links for builder breakdowns to the first column.
298 if (tableId == "buildstep_div_table" && j == 0) {
299 var a = document.createElement("a");
300 a.href = "#";
301 a.buildStepName = value;
302 a.addEventListener("click", function(event) {
303 var name = event.target.buildStepName;
304 loadGraphiteData(null, null, null, null, name,
305 name + " on ...", "builder_div",
306 GROUP_NODE_BUILDER);
307 });
308 a.innerHTML = value;
309 td.appendChild(a);
310 } else {
311 td.innerHTML = value;
312 }
313 tr.appendChild(td);
314 }
315 table.appendChild(tr);
316 }
317 var container = document.getElementById(containerId);
318 container.innerHTML = "";
319 table.id = tableId;
320 var h2 = document.createElement("h2");
321 h2.style.textAlign = "center";
322 h2.innerHTML = title;
323 container.appendChild(h2);
324 container.appendChild(table);
325 }
326
327 /**
328 * Callback function for receiving data from Graphite. Organizes the data and
329 * rebuilds the data table.
330 *
331 * @param {Array<Object>} data Data obtained from Graphite.
332 */
333 function gotGraphiteData(data) {
334 var loadingData = loadingDone();
335 makeTableFromData(data, loadingData["tableTitle"],
336 loadingData["containerId"]);
337 }
338
339 /**
340 * Helper function for building Graphite data URLs. If any of the parameters
341 * are not provided, it obtains defaults from the text boxes on the page.
342 *
343 * @param {string} timeStart Unix timestamp; time period beginning.
344 * @param {string} timeEnd Unix timestamp; time period end.
345 * @param {string} masterFilter Filter the data by build master.
346 * @param {string} builderFilter Filter the data by builder.
347 * @param {string} buildStepFilter Filter the data by build step.
348 * @param {number} groupNode Column index for grouping Graphite data.
349 */
350 function makeGraphiteURL(timeStart, timeEnd, masterFilter, builderFilter,
351 buildStepFilter, groupNode) {
352 if (!timeStart) {
353 timeStart =
354 dateTimeLocalToUnixUTC(document.getElementById("time_start").value);
355 }
356 if (!timeEnd) {
357 timeEnd =
358 dateTimeLocalToUnixUTC(document.getElementById("time_end").value);
359 }
360 if (!masterFilter) {
361 masterFilter = document.getElementById("master_filter").value;
362 }
363 if (!builderFilter) {
364 builderFilter = document.getElementById("builder_filter").value;
365 }
366 if (!buildStepFilter) {
367 buildStepFilter = document.getElementById("buildstep_filter").value;
368 }
369 var url = "http://skiamonitor.com/render?format=json&from=" +
370 timeStart + "&until=" + timeEnd;
371 var metricPrefixParts = ["buildbot", masterFilter, builderFilter,
372 buildStepFilter]
373 var metricPrefix = metricPrefixParts.join(".");
374
375 for (var i = 0; i < metrics.length; ++i) {
376 var metric = metrics[i][0];
377 var summaryMode = metrics[i][1];
378 var fullMetricName = metricPrefix + "." + metric;
379 var groupByNode = "groupByNode(" + fullMetricName + "," + groupNode +
380 ",%22" + summaryMode + "%22)";
381 var summarize = "summarize(" + groupByNode + ",%22" + SUMMARIZE_INTERVAL +
382 "%22,%22" + summaryMode + "%22,true)";
383 var aliasByNode = "aliasByNode(" + summarize + ",0)";
384 var aliasSub = "aliasSub(" + aliasByNode + ",%22$%22,%22." + metric +
385 "%22)";
386 var fullTarget = "&target=" + aliasSub;
387 url += fullTarget;
388 }
389 return url;
390 }
391
392 /**
393 * Load data from Graphite.
394 *
395 * @param {string} timeStart Unix timestamp; time period beginning.
396 * @param {string} timeEnd Unix timestamp; time period end.
397 * @param {string} masterFilter Filter the data by build master.
398 * @param {string} builderFilter Filter the data by builder.
399 * @param {string} buildStepFilter Filter the data by build step.
400 * @param {string} tableTitle Title for the table.
401 * @param {string} containerId ID of the element which will hold the table.
402 * @param {number} groupNode Column index for grouping Graphite data.
403 */
404 function loadGraphiteData(timeStart, timeEnd, masterFilter, builderFilter,
405 buildStepFilter, tableTitle, containerId, groupNode) {
406 var url = makeGraphiteURL(timeStart, timeEnd, masterFilter, builderFilter,
407 buildStepFilter, groupNode);
408 var data = {"tableTitle": tableTitle, "containerId": containerId};
409 loadingStart(url, data);
410 loadJSONP(url, "gotGraphiteData");
411 }
412
413 /**
414 * Function called when the "Load data" button is clicked.
415 */
416 function reloadWithParams() {
417 var newQuery =
418 "from=" + dateTimeLocalToUnixUTC(document.getElementById("time_start").v alue) +
419 "&until=" + dateTimeLocalToUnixUTC(document.getElementById("time_end").v alue) +
420 "&master=" + document.getElementById("master_filter").value +
421 "&builder=" + document.getElementById("builder_filter").value +
422 "&buildstep=" + document.getElementById("buildstep_filter").value;
423 window.location.search = newQuery;
424 }
425
426 /**
427 * Get the local timezone offset in seconds.
428 */
429 function getTimezoneOffset() {
430 return new Date().getTimezoneOffset() * 60;
431 }
432
433 /**
434 * Convert UTC to Local time.
435 */
436 function UTCToLocal(unixDate) {
437 return unixDate - getTimezoneOffset();
438 }
439
440 /**
441 * Convert Local to UTC time.
442 */
443 function localToUTC(unixDate) {
444 return unixDate + getTimezoneOffset();
445 }
446
447 /**
448 * Get the date in Unix format.
449 */
450 function unixDate(date) {
451 var dateObj = null;
452 if (!date) {
453 dateObj = new Date()
454 } else {
455 dateObj = new Date(date);
456 }
457 return dateObj.getTime() / 1000;
458 }
459
460 /**
461 * Convert from RFC 3339 to Unix format.
462 */
463 function RFC3339ToUnix(date) {
464 return unixDate(date);
465 }
466
467 /**
468 * Convert from Unix to RFC 3339 format, with no time zone information.
469 */
470 function unixToRFC3339(timestamp) {
471 var date = new Date(parseInt(timestamp) * 1000);
472 function zfill(num) {
473 if (num < 10) {
474 return "0" + num;
475 } else {
476 return num;
477 }
478 }
479 return (date.getUTCFullYear() + "-" +
480 zfill(date.getUTCMonth() + 1) + "-" +
481 zfill(date.getUTCDate()) + "T" +
482 zfill(date.getUTCHours()) + ":" +
483 zfill(date.getUTCMinutes()) + ":" +
484 zfill(date.getUTCSeconds()));
485 }
486
487 /**
488 * Convert from Unix timestamp to datetime-local RFC 3339 format.
489 */
490 function unixUTCToDateTimeLocal(unix) {
491 return unixToRFC3339(UTCToLocal(unix));
492 }
493
494 /**
495 * Convert from datetime-local RFC 3339 format to Unix timestamp.
496 */
497 function dateTimeLocalToUnixUTC(rfcLocal) {
498 return localToUTC(RFC3339ToUnix(rfcLocal));
499 }
500
501 /**
502 * Function called on page load.
503 */
504 function init() {
505 // Load default query values.
506 var currentTime = unixDate();
507 var timeEnd = currentTime;
508 var timeStart = currentTime - DEFAULT_TIME_PERIOD;
509 var masterFilter = DEFAULT_MASTER_FILTER;
510 var builderFilter = DEFAULT_BUILDER_FILTER;
511 var buildstepFilter = DEFAULT_BUILDSTEP_FILTER;
512
513 // Retrieve any parameters from the URL.
514 var params = window.location.search.substring(1).split("&");
515 // Remove empty string from parameters.
516 while (params.indexOf("") != -1) {
517 params.splice(params.indexOf(""));
518 }
519 for (var i = 0; i < params.length; ++i) {
520 var splitParam = params[i].split("=");
521 if (splitParam[0] == "from") {
522 timeStart = splitParam[1];
523 } else if (splitParam[0] == "until") {
524 timeEnd = splitParam[1];
525 } else if (splitParam[0] == "master") {
526 masterFilter = splitParam[1];
527 } else if (splitParam[0] == "builder") {
528 builderFilter = splitParam[1];
529 } else if (splitParam[0] == "buildstep") {
530 buildstepFilter = splitParam[1];
531 } else {
532 console.error("Unknown parameter: " + splitParam[0]);
533 }
534 }
535
536 // Fill the form with the appropriate values.
537 document.getElementById("time_start").value =
538 unixUTCToDateTimeLocal(timeStart);
539 document.getElementById("time_end").value =
540 unixUTCToDateTimeLocal(timeEnd);
541 document.getElementById("master_filter").value = masterFilter;
542 document.getElementById("builder_filter").value = builderFilter;
543 document.getElementById("buildstep_filter").value = buildstepFilter;
544
545 // Decide what to do, based on the number of provided parameters.
546 if (params.length == 0) {
547 // Don't load data; we assume that the user might want to tweak the
548 // filters first.
549 } else if (params.length != 5) {
550 // Reload the page, filling in the rest of the parameters with their
551 // defaults.
552 reloadWithParams();
553 } else {
554 // All parameters were provided. Load data.
555 loadGraphiteData(timeStart, timeEnd, masterFilter, builderFilter,
556 buildstepFilter, "Build Steps", "buildstep_div",
557 GROUP_NODE_BUILDSTEP);
558 }
559 }
560
561 </script>
562 </head>
563 <body>
564 <div id="heading" style="font-size:2.5em; text-align:center; height:7%;">
565 Skia Buildstep Dashboard
566 </div>
567 <div style="text-align:center;">
568 Click a column header to sort that column. Click a buildstep to see its
569 results broken down into builders.
570 </div>
571 <div id="main_content_area" style="width:100%; height:90%; padding:0px;
572 margin:0px;">
573 <div id="menu_div"
574 style="float:left; width:18%; height:100%; padding:0px; margin:0px;">
575 <div style="width:100%;">
576 <nobr>Time period:</nobr><br/>
577 From:<br/>
578 <input type="datetime-local" id="time_start" /><br/>
579 To:<br/>
580 <input type="datetime-local" id="time_end" /><br/>
581 <nobr>Build master filter:</nobr><br/>
582 <input type="text" id="master_filter" /><br/>
583 <nobr>Builder filter:</nobr><br/>
584 <input type="text" id="builder_filter" /><br/>
585 <nobr>Build step filter:</nobr><br/>
586 <input type="text" id="buildstep_filter" /><br/>
587 <input type="button" id="load_data_button" onClick="reloadWithParams() ;" value="Load Data"/>
588 </div>
589 <div id="logging_div" style="width:100%; padding:0px; margin:0px"></div>
590 </div>
591 <div id="buildstep_div"
592 style="float:left; width:41%; padding:0px; margin:0px">
593 </div>
594 <div id="builder_div"
595 style="float:left; width:41%; padding:0px; margin:0px">
596 </div>
597 </div>
598 <script type="text/javascript">init();</script>
599 </body>
600 </html>
601
OLDNEW
« no previous file with comments | « master/public_html/buildslave_idle.html ('k') | master/public_html/default.css » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698