summaryrefslogtreecommitdiffstats
path: root/tools/perf
diff options
context:
space:
mode:
authorrubentopo@gmail.com <rubentopo@gmail.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-09 21:16:54 +0000
committerrubentopo@gmail.com <rubentopo@gmail.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-09 21:16:54 +0000
commit68ac87d689deb6c676d122591b49bd529c175375 (patch)
treefe7ef902c11e45cfe35f87cb569d2d6558f9ab03 /tools/perf
parent86335314c3230915d78494d89b25d3ae79520151 (diff)
downloadchromium_src-68ac87d689deb6c676d122591b49bd529c175375.zip
chromium_src-68ac87d689deb6c676d122591b49bd529c175375.tar.gz
chromium_src-68ac87d689deb6c676d122591b49bd529c175375.tar.bz2
[telemetry] Rough prototype of results viewer
Results viewer tool for the scrolling benchmarks csv output. Given a csv file, displays it's contents, upon column selection graphs it's values. BUG= TEST= Review URL: https://chromiumcodereview.appspot.com/11411214 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@172016 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/perf')
-rw-r--r--tools/perf/utils/results_viewer/src/base.js84
-rw-r--r--tools/perf/utils/results_viewer/src/main.css13
-rw-r--r--tools/perf/utils/results_viewer/src/main.js293
-rw-r--r--tools/perf/utils/results_viewer/src/results_viewer.html39
-rw-r--r--tools/perf/utils/results_viewer/src/results_viewer.js51
-rw-r--r--tools/perf/utils/results_viewer/src/ui.js83
6 files changed, 563 insertions, 0 deletions
diff --git a/tools/perf/utils/results_viewer/src/base.js b/tools/perf/utils/results_viewer/src/base.js
new file mode 100644
index 0000000..df65c76
--- /dev/null
+++ b/tools/perf/utils/results_viewer/src/base.js
@@ -0,0 +1,84 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+
+/**
+ * The global object.
+ * @type {!Object}
+ * @const
+ */
+var global = this;
+
+
+/** Platform, package, object property, and Event support. */
+this.base = (function() {
+
+ function mLog(text, opt_indentLevel) {
+ if (true)
+ return;
+
+ var spacing = '';
+ var indentLevel = opt_indentLevel || 0;
+ for (var i = 0; i < indentLevel; i++)
+ spacing += ' ';
+ console.log(spacing + text);
+ }
+
+ /**
+ * Builds an object structure for the provided namespace path,
+ * ensuring that names that already exist are not overwritten. For
+ * example:
+ * 'a.b.c' -> a = {};a.b={};a.b.c={};
+ * @param {string} name Name of the object that this file defines.
+ * @param {*=} opt_object The object to expose at the end of the path.
+ * @param {Object=} opt_objectToExportTo The object to add the path to;
+ * default is {@code global}.
+ * @private
+ */
+ function exportPath(name, opt_object, opt_objectToExportTo) {
+ var parts = name.split('.');
+ var cur = opt_objectToExportTo || global;
+
+ for (var part; parts.length && (part = parts.shift());) {
+ if (!parts.length && opt_object !== undefined) {
+ // last part and we have an object; use it
+ cur[part] = opt_object;
+ } else if (part in cur) {
+ cur = cur[part];
+ } else {
+ cur = cur[part] = {};
+ }
+ }
+ return cur;
+ }
+
+ function exportTo(namespace, fn) {
+ var obj = exportPath(namespace);
+ try {
+ var exports = fn();
+ } catch (e) {
+ console.log('While running exports for ', name, ':');
+ console.log(e.stack || e);
+ return;
+ }
+
+ for (var propertyName in exports) {
+ // Maybe we should check the prototype chain here? The current usage
+ // pattern is always using an object literal so we only care about own
+ // properties.
+ var propertyDescriptor = Object.getOwnPropertyDescriptor(exports,
+ propertyName);
+ if (propertyDescriptor) {
+ Object.defineProperty(obj, propertyName, propertyDescriptor);
+ mLog(' +' + propertyName);
+ }
+ }
+ }
+
+ return {
+ exportTo: exportTo
+ };
+})(); \ No newline at end of file
diff --git a/tools/perf/utils/results_viewer/src/main.css b/tools/perf/utils/results_viewer/src/main.css
new file mode 100644
index 0000000..8bb8729
--- /dev/null
+++ b/tools/perf/utils/results_viewer/src/main.css
@@ -0,0 +1,13 @@
+/**
+ * Copyright (c) 2012 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+.yui-dt, .viewer {
+ /*Viewer extends yui-dt class*/
+}
+
+.viewer {
+ /*Implementation goes here*/
+}
diff --git a/tools/perf/utils/results_viewer/src/main.js b/tools/perf/utils/results_viewer/src/main.js
new file mode 100644
index 0000000..a889387
--- /dev/null
+++ b/tools/perf/utils/results_viewer/src/main.js
@@ -0,0 +1,293 @@
+/**
+ * Copyright (c) 2012 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+'use strict';
+
+(function() {
+ /**
+ * Main results viewer component.
+ */
+ var Viewer = ui.define('div');
+ (function() {
+ //"Private" functions for Viewer
+ /**
+ * Determines the appropriate parser for a given column, using it's first
+ * data element.
+ * @param {String} firstElement The first (non-header) element of a given
+ * column.
+ * @return {String} The YUI parser needed for the column.
+ */
+ function getColumnParser(firstElement) {
+ if (isNumeric(firstElement)) {
+ return 'number';
+ } else if (isDate(firstElement)) {
+ return 'date';
+ } else {
+ return 'string';
+ }
+ }
+
+ /**
+ * Determines whether or not the given element is a date.
+ * @param {String} str The string representation of a potential date.
+ * @return {boolean} true/false whether or not str can be parsed to
+ * a date.
+ */
+ function isDate(str) {
+ var timestamp = Date.parse(str);
+ return !isNaN(timestamp);
+ }
+
+ /**
+ * Generates the YUI column definition for the given dataset.
+ * @param {String[][]} dataSet the dataset that will be displayed.
+ * @return {JSON[]} The array containing the column definitions.
+ */
+ function createYUIColumnDefinitions(dataSet) {
+ var header = dataSet[0];
+ var firstRow = dataSet[1];
+ var columnDefinitions = [];
+ header.forEach(function (element, index, array) {
+ columnDefinitions.push({
+ 'key' : index.toString(),
+ 'label':element.toString(),
+ 'maxAutoWidth':95,
+ 'sortable':true,
+ 'parser':getColumnParser(firstRow[index])});
+ });
+ return columnDefinitions;
+ }
+
+ /**
+ * Generates the YUI data source for the given dataset.
+ * @param {String[][]} dataSet the dataset that will be displayed.
+ * @return {YAHOO.util.FunctionDataSource} The YUI data source
+ * derived from the dataset.
+ */
+ function createYUIDataSource(dataSet) {
+ var dataSource = [];
+ //Starts from the first non-header line.
+ for (var i = 1; i < dataSet.length; i++) {
+ var dataSourceLine = {};
+ dataSet[i].forEach(function (element, index, array) {
+ if (isNumeric(element)) {
+ dataSourceLine[index.toString()] = parseFloat(element);
+ } else {
+ dataSourceLine[index.toString()] = element
+ }
+ });
+ dataSource.push(dataSourceLine);
+ }
+ return new YAHOO.util.FunctionDataSource(function() {
+ return dataSource});
+ }
+
+ /**
+ * Un-selects all the columns from the given data table.
+ * @param {YAHOO.widget.DataTable} dataTable The data table that
+ * contains the results.
+ */
+ function unselectAllColumns(dataTable) {
+ var selectedColumns = dataTable.getSelectedColumns();
+ for (var i = 0; i < selectedColumns.length; i++) {
+ dataTable.unselectColumn(selectedColumns[i]);
+ }
+ }
+
+ /**
+ * Generates an array that contains the indices of the selected
+ * columns in the data table.
+ * @param {YAHOO.widget.DataTable} dataTable
+ * @return {int[]} An array with the indices of the selected columns.
+ */
+ function getSelectedColumnIndices(dataTable) {
+ var selectedColumnIndices = [];
+ var selectedColumns = dataTable.getSelectedColumns();
+ for (var i = 0; i < selectedColumns.length; i++) {
+ selectedColumnIndices.push(selectedColumns[i].key);
+ }
+ return selectedColumnIndices;
+ }
+
+ Viewer.prototype = {
+ __proto__: HTMLDivElement.prototype,
+ decorate:function() {
+ /**
+ * The id for the element that contains the barchart (Optional).
+ * @type {String}
+ */
+ this.barChartElementId_ = undefined;
+ /**
+ * The rectangular array that contains the contents of the cvs file.
+ * @type {String[][]}
+ */
+ this.dataSet_ = undefined;
+ },
+ set barChartElementId(e) {
+ this.barChartElementId_ = e;
+ },
+ get barChartElementId() {
+ return this.barChartElementId_;
+ },
+ set dataSet(ds) {
+ this.dataSet_ = ds;
+ },
+ get dataSet() {
+ return this.dataSet_;
+ },
+ /**
+ * Renders the Viewer component.
+ * @expose
+ */
+ render: function() {
+ document.body.appendChild(this);
+ var previousBarChart = this.barChartElementId_ != null ?
+ $(this.barChartElementId_) : null;
+ if (previousBarChart != null) {
+ document.body.removeChild(previousBarChart);
+ window.location.hash = this.id;
+ }
+
+ var columnDefinitions = createYUIColumnDefinitions(this.dataSet_);
+ var dataSource = createYUIDataSource(this.dataSet_);
+ var dataTable = new YAHOO.widget.DataTable(this.id, columnDefinitions,
+ dataSource, {caption:'Results'});
+ var firstRow = this.dataSet_[1];
+ var currentViewer = this;
+
+ dataTable.subscribe('cellClickEvent', function (oArgs) {
+ var selectedColumn = dataTable.getColumn(oArgs.target);
+ var selectedColumnIndex = parseInt(selectedColumn.key);
+
+ if (selectedColumnIndex == 0) {
+ unselectAllColumns(dataTable);
+ return;
+ }
+
+ if (isNumeric(firstRow[selectedColumnIndex])) {
+ dataTable.selectColumn(selectedColumn);
+ if (currentViewer.barChartElementId_ != null) {
+ var viewerBarChart_ =
+ new ViewerBarChart({ownerDocument:window.document});
+ viewerBarChart_.id = currentViewer.barChartElementId_;
+ viewerBarChart_.dataSet = currentViewer.dataSet_;
+ viewerBarChart_.selectedColumnIndices
+ = getSelectedColumnIndices(dataTable);
+ viewerBarChart_.render();
+ }
+ }
+ });
+ }
+ };
+ }());
+
+ /**
+ * BarChart component for the results viewer.
+ */
+ var ViewerBarChart = ui.define('div');
+ (function () {
+ //"Private" functions for ViewerBarChart
+ /**
+ * Generates a new array that contains only the first column, and all
+ * other selected columns.
+ * @param {(string|number)[][]} dataset Array with the csv contents.
+ * @param {int[]} selectedColumnIndices Indices for all the selected
+ * columns.
+ * @return {String[][]} A new array containing the first column
+ * and all selected columns.
+ */
+ function extractColumnsToPlot(dataset, selectedColumnIndices) {
+ var lines = [];
+ var line = [];
+ for (var i = 0; i < dataset.length; ++i) {
+ line.push(dataset[i][0]);
+ for (var j = 0; j < selectedColumnIndices.length; j++) {
+ var elementValue = dataset[i][selectedColumnIndices[j]];
+ line.push(isNumeric(elementValue) ? parseFloat(elementValue) :
+ elementValue);
+ }
+ lines.push(line);
+ line = [];
+ }
+ return lines;
+ }
+
+ ViewerBarChart.prototype = {
+ __proto__:HTMLDivElement.prototype,
+ decorate: function() {
+ /**
+ * Percetage of the window width that will be used for the chart
+ * @const
+ * @type {float}
+ */
+ ViewerBarChart.PERCENTAGE_OF_WINDOW_WIDTH_FOR_CHART = 0.75;
+ /**
+ * Approximate number of pixels that will be used per line
+ * @const
+ * @type {int}
+ */
+ ViewerBarChart.HEIGHT_PER_LINE_IN_PIXELS = 28;
+
+ /**
+ * Raw dataset, which contains the csv file contents.
+ * @type {(String|number)[][]}
+ */
+ this.dataSet_ = undefined;
+ /**
+ * Array that contains the selected indices from the table view.
+ * @type {number[]}
+ */
+ this.selectedColumnIndices_ = undefined;
+ },
+ /**
+ * Renders the ViewerBarChart component.
+ * @expose
+ */
+ render : function() {
+ var existingBarChart = $(this.id);
+ if (existingBarChart != null) {
+ //Remove the previous bar chart
+ document.body.removeChild(existingBarChart);
+ }
+ //Attach this component to the document
+ document.body.appendChild(this);
+ var lines = extractColumnsToPlot(this.dataSet_,
+ this.selectedColumnIndices_);
+ var data = google.visualization.arrayToDataTable(lines);
+
+ var barCharWidth = window.width *
+ ViewerBarChart.PERCENTAGE_OF_WINDOW_WIDTH_FOR_CHART;
+ var barCharHeight = this.dataSet_.length *
+ ViewerBarChart.HEIGHT_PER_LINE_IN_PIXELS;
+ var options = {
+ 'width': barCharWidth,
+ 'height':barCharHeight,
+ 'fontSize':15
+ };
+ new google.visualization.BarChart(this).draw(data, options);
+ window.location.hash = this.id;
+ },
+ set dataSet(ds) {
+ this.dataSet_ = ds;
+ },
+ set selectedColumnIndices(sci) {
+ this.selectedColumnIndices_ = sci;
+ }
+ };
+ }());
+
+ /**
+ * Determines whether or not a string can be parsed to a number.
+ * @param {String} element String representation of the potential number.
+ * @return {boolean} True or false depending on whether the element is
+ * numeric or not.
+ */
+ function isNumeric(element) {
+ return !isNaN(parseFloat(element)) && isFinite(element);
+ }
+
+ window.Viewer = Viewer;
+ window.ViewerBarChart = ViewerBarChart;
+})(); \ No newline at end of file
diff --git a/tools/perf/utils/results_viewer/src/results_viewer.html b/tools/perf/utils/results_viewer/src/results_viewer.html
new file mode 100644
index 0000000..64554ba
--- /dev/null
+++ b/tools/perf/utils/results_viewer/src/results_viewer.html
@@ -0,0 +1,39 @@
+<!--
+Copyright (c) 2012 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<!DOCTYPE HTML>
+<html i18n-values="dir:textdirection">
+ <head>
+ <meta charset="utf-8">
+ <title>
+ Results viewer
+ </title>
+ <link type="text/css" rel="stylesheet" href="http://yui.yahooapis.com/2.9.0/build/datatable/assets/skins/sam/datatable.css">
+ <link type="text/css" rel="stylesheet" href="main.css">
+ <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/prototype/1.7.0.0/prototype.js"></script>
+ <script src="http://yui.yahooapis.com/2.9.0/build/yahoo-dom-event/yahoo-dom-event.js"></script>
+ <script src="http://yui.yahooapis.com/2.9.0/build/element/element-min.js"></script>
+ <script src="http://yui.yahooapis.com/2.9.0/build/datasource/datasource-min.js"></script>
+ <script src="http://yui.yahooapis.com/2.9.0/build/datatable/datatable-min.js"></script>
+ <script type="text/javascript" src="https://www.google.com/jsapi"></script>
+ <script type="text/javascript">
+ google.load("visualization", "1", {packages:["corechart"]});
+ </script>
+ <script type="text/javascript" src="base.js"></script>
+ <script type="text/javascript" src="ui.js"></script>
+ <script type="text/javascript" src="results_viewer.js"></script>
+ <script type="text/javascript" src="main.js"></script>
+ </head>
+ <body class="yui-skin-sam">
+ <form action="">
+ <input type="file" id="fileSelector" name="files[]" />
+ </form>
+ <!--<div id="csvContentsDataTable" class="viewer"></div> TODO remove me!-->
+ <!--<div id="barChart"></div> TODO remove me-->
+ </body>
+</html>
+<script type="text/javascript">
+ $('fileSelector').addEventListener('change', handleFileSelectEvent, false);
+</script> \ No newline at end of file
diff --git a/tools/perf/utils/results_viewer/src/results_viewer.js b/tools/perf/utils/results_viewer/src/results_viewer.js
new file mode 100644
index 0000000..06783c4
--- /dev/null
+++ b/tools/perf/utils/results_viewer/src/results_viewer.js
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2012 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+'use strict';
+
+/**
+ * Main entry point for the rendering of the Viewer component.
+ * @param {Event} evt File select event.
+ */
+function handleFileSelectEvent(evt) {
+ var file = evt.target.files[0];
+ var fileReader = new FileReader();
+
+ fileReader.onload = (function(theFile) {
+ return function(e) {
+ var dataSet = createViewerDataset(e.target.result);
+ var viewer = new Viewer({ownerDocument:window.document});
+ viewer.id = 'csvContentsDataTable';
+ viewer.dataSet = dataSet;
+ viewer.barChartElementId = 'barChart';
+ viewer.render();
+ };
+ })(file);
+ fileReader.readAsText(file);
+}
+
+/**
+ * Splits the csv file contents, and creates a square array of strings for
+ * each token in the line.
+ * @param fileContents
+ * @return {String[][]} The data set with the contents of the csv file.
+ */
+function createViewerDataset(fileContents) {
+ var rows = fileContents.split('\n');
+ var dataset = [];
+ rows.forEach(function (row, index, array) {
+ if (row.trim().length == 0) {
+ //Ignore empty lines
+ return;
+ }
+ var newRow = [];
+ var columns = row.split(',');
+ columns.forEach(function (element, index, array) {
+ newRow.push(element);
+ });
+ dataset.push(newRow);
+ });
+ return dataset;
+} \ No newline at end of file
diff --git a/tools/perf/utils/results_viewer/src/ui.js b/tools/perf/utils/results_viewer/src/ui.js
new file mode 100644
index 0000000..576e87b
--- /dev/null
+++ b/tools/perf/utils/results_viewer/src/ui.js
@@ -0,0 +1,83 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+base.exportTo('ui', function() {
+
+ /**
+ * Helper function for creating new element for define.
+ */
+ function createElementHelper(tagName, opt_bag) {
+ // Allow passing in ownerDocument to create in a different document.
+ var doc;
+ if (opt_bag && opt_bag.ownerDocument)
+ doc = opt_bag.ownerDocument;
+
+ return doc.createElement(tagName);
+ }
+
+ /**
+ * Creates the constructor for a UI element class.
+ *
+ * Usage:
+ * <pre>
+ * var List = base.ui.define('list');
+ * List.prototype = {
+ * __proto__: HTMLUListElement.prototype,
+ * decorate: function() {
+ * ...
+ * },
+ * ...
+ * };
+ * </pre>
+ *
+ * @param {string|Function} tagNameOrFunction The tagName or
+ * function to use for newly created elements. If this is a function it
+ * needs to return a new element when called.
+ * @return {function(Object=):Element} The constructor function which takes
+ * an optional property bag. The function also has a static
+ * {@code decorate} method added to it.
+ */
+ function define(tagNameOrFunction) {
+ var createFunction, tagName;
+ if (typeof tagNameOrFunction == 'function') {
+ createFunction = tagNameOrFunction;
+ tagName = '';
+ } else {
+ createFunction = createElementHelper;
+ tagName = tagNameOrFunction;
+ }
+
+ /**
+ * Creates a new UI element constructor.
+ * @param {Object=} opt_propertyBag Optional bag of properties to set on the
+ * object after created. The property {@code ownerDocument} is special
+ * cased and it allows you to create the element in a different
+ * document than the default.
+ * @constructor
+ */
+ function f(opt_propertyBag) {
+ var el = createFunction(tagName, opt_propertyBag);
+ f.decorate(el);
+ for (var propertyName in opt_propertyBag) {
+ el[propertyName] = opt_propertyBag[propertyName];
+ }
+ return el;
+ }
+
+ /**
+ * Decorates an element as a UI element class.
+ * @param {!Element} el The element to decorate.
+ */
+ f.decorate = function(el) {
+ el.__proto__ = f.prototype;
+ el.decorate();
+ };
+
+ return f;
+ }
+
+ return {
+ define: define
+ };
+});