profiler.js revision effb81e5f8246d0db0270817048dc992db66e9fb
1de69a4ca2fd66cc600116a10bef1f3ab1610d62cChris Lattner// Copyright 2014 The Chromium Authors. All rights reserved. 2fd93908ae8b9684fe71c239e3c6cfe13ff6a2663Misha Brukman// Use of this source code is governed by a BSD-style license that can be 3b576c94c15af9a440f69d9d03c2afead7971118cJohn Criswell// found in the LICENSE file. 4b576c94c15af9a440f69d9d03c2afead7971118cJohn Criswell 54ee451de366474b9c228b4e5fa573795a715216dChris Lattner// This module handles the UI for the profiling. A memory profile is obtained 64ee451de366474b9c228b4e5fa573795a715216dChris Lattner// asking the webservice to profile some data sources (through /profile/create) 7fd93908ae8b9684fe71c239e3c6cfe13ff6a2663Misha Brukman// as, for instance, some mmaps dumps or some heap traces. 8b576c94c15af9a440f69d9d03c2afead7971118cJohn Criswell// Regardless of the data source, a profile consists of three main concepts: 9de69a4ca2fd66cc600116a10bef1f3ab1610d62cChris Lattner// 1. A tree of buckets, each one having a name and a set of 1+ values (see 10de69a4ca2fd66cc600116a10bef1f3ab1610d62cChris Lattner// /classification/rules.py,results.py), one value per metric (see below). 11de69a4ca2fd66cc600116a10bef1f3ab1610d62cChris Lattner// 2. A set of snapshots, identifying the times when the dump was taken. All the 12de69a4ca2fd66cc600116a10bef1f3ab1610d62cChris Lattner// snapshots have the same shape and the same nodes (but their values can 13de69a4ca2fd66cc600116a10bef1f3ab1610d62cChris Lattner// obviously differ). 1436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines// 3. A set of metrics, identifying the cardinality (how many) and the semantic 15d04a8d4b33ff316ca4cf961e06c9e312eff8e64fChandler Carruth// (what do they mean) of the values of the nodes in the results tree. 169d7f0b76aac3bf0524e9285101e34c15e1e2c599Owen Anderson// 170b8c9a80f20772c3793201ab5b251d3520b9cea3Chandler Carruth// From a graphical viewpoint a profile is displayed using two charts: 18e81561909d128c6e2d8033cb5465a49b2596b26aBill Wendling// - A tree (organizational) chart which shows, for a given snapshot and metric, 1992f2c871105a1ae5e00655a2f731b6bab3983621Owen Anderson// the taxonomy of the buckets and their corresponding value. 201f6efa3996dd1929fbc129203ce5009b620e6969Michael J. Spencer// - A time series (scattered area) chart which shows, for a given metric and a 211f6efa3996dd1929fbc129203ce5009b620e6969Michael J. Spencer// given bucket, its evolution over time (and of its direct children). 222cdd21c2e4d855500dfb53f77aa74da53ccf9de6Chris Lattner 23d0fde30ce850b78371fd1386338350591f9ff494Brian Gaekeprofiler = new (function() { 24c34ebf65af0139eaf5cb0969fabcd32c0b6e1710Owen Anderson 25c34ebf65af0139eaf5cb0969fabcd32c0b6e1710Owen Andersonthis.treeData_ = null; 26ce36d55cf8d3239942e0e9c426c835f0c33a11e6Chris Lattnerthis.treeChart_ = null; 27c34ebf65af0139eaf5cb0969fabcd32c0b6e1710Owen Andersonthis.timeSeriesData_ = null; 28c34ebf65af0139eaf5cb0969fabcd32c0b6e1710Owen Andersonthis.timeSeriesChart_ = null; 29c34ebf65af0139eaf5cb0969fabcd32c0b6e1710Owen Andersonthis.isRedrawing_ = false; 30de69a4ca2fd66cc600116a10bef1f3ab1610d62cChris Lattnerthis.profileId_ = null; // The profile id retrieved on /ajax/profile/create. 31b663d76f866d03756dd563d246d9bdaabca4ca33Alkis Evlogimenosthis.times_ = []; // Snapshot times: [0,4,8] -> 3 snapshots x 4 sec. 32b663d76f866d03756dd563d246d9bdaabca4ca33Alkis Evlogimenosthis.metrics_ = []; // Keys in the result tree, e.g., ['RSS', 'PSS']. 33c34ebf65af0139eaf5cb0969fabcd32c0b6e1710Owen Andersonthis.curTime_ = null; // Time of the snapshot currently displayed. 3492f2c871105a1ae5e00655a2f731b6bab3983621Owen Andersonthis.curMetric_ = null; // Index (rel. to |metrics_|) currently displayed. 35b663d76f866d03756dd563d246d9bdaabca4ca33Alkis Evlogimenosthis.curBucket_ = null; // Index (rel. to the tree) currently displayed. 36b663d76f866d03756dd563d246d9bdaabca4ca33Alkis Evlogimenos 37b663d76f866d03756dd563d246d9bdaabca4ca33Alkis Evlogimenosthis.onDomReady_ = function() { 38c34ebf65af0139eaf5cb0969fabcd32c0b6e1710Owen Anderson this.treeChart_ = new google.visualization.OrgChart($('#prof-tree_chart')[0]); 39c34ebf65af0139eaf5cb0969fabcd32c0b6e1710Owen Anderson this.timeSeriesChart_ = new google.visualization.SteppedAreaChart( 40b663d76f866d03756dd563d246d9bdaabca4ca33Alkis Evlogimenos $('#prof-time_chart')[0]); 41b663d76f866d03756dd563d246d9bdaabca4ca33Alkis Evlogimenos 42b663d76f866d03756dd563d246d9bdaabca4ca33Alkis Evlogimenos // TODO(primiano): De-hardcodify the ruleset and list/load from the server. 43c34ebf65af0139eaf5cb0969fabcd32c0b6e1710Owen Anderson $('#prof-ruleset').append( 4492f2c871105a1ae5e00655a2f731b6bab3983621Owen Anderson $('<option>').val('default/mmap-android.py').text('mmap-Android')); 45b663d76f866d03756dd563d246d9bdaabca4ca33Alkis Evlogimenos 46b663d76f866d03756dd563d246d9bdaabca4ca33Alkis Evlogimenos // Setup the UI event listeners to trigger the onUiParamsChange_ event. 47b663d76f866d03756dd563d246d9bdaabca4ca33Alkis Evlogimenos google.visualization.events.addListener(this.treeChart_, 'select', 48c34ebf65af0139eaf5cb0969fabcd32c0b6e1710Owen Anderson this.onUiParamsChange_.bind(this)); 49c34ebf65af0139eaf5cb0969fabcd32c0b6e1710Owen Anderson $('#prof-metric').on('change', this.onUiParamsChange_.bind(this)); 50b663d76f866d03756dd563d246d9bdaabca4ca33Alkis Evlogimenos $('#prof-time').slider({range: 'max', min: 0, max: 0, value: 0, 51b663d76f866d03756dd563d246d9bdaabca4ca33Alkis Evlogimenos change: this.onUiParamsChange_.bind(this)}); 52c34ebf65af0139eaf5cb0969fabcd32c0b6e1710Owen Anderson}; 53c34ebf65af0139eaf5cb0969fabcd32c0b6e1710Owen Anderson 54c34ebf65af0139eaf5cb0969fabcd32c0b6e1710Owen Andersonthis.profileCachedMmapDump = function(mmapDumpId) { 55c34ebf65af0139eaf5cb0969fabcd32c0b6e1710Owen Anderson // Creates a profile using the data grabbed during a recent mmap dump. 56c34ebf65af0139eaf5cb0969fabcd32c0b6e1710Owen Anderson // This is used to get a quick overview (only one snapshot), of the memory 5792f2c871105a1ae5e00655a2f731b6bab3983621Owen Anderson // without doing a full periodic trace first. 58c34ebf65af0139eaf5cb0969fabcd32c0b6e1710Owen Anderson webservice.ajaxRequest('/profile/create', // This is a POST request. 5992f2c871105a1ae5e00655a2f731b6bab3983621Owen Anderson this.onProfileAjaxResponse_.bind(this), 60b663d76f866d03756dd563d246d9bdaabca4ca33Alkis Evlogimenos null, // use the default error handler. 6192f2c871105a1ae5e00655a2f731b6bab3983621Owen Anderson {type: 'mmap', 62c34ebf65af0139eaf5cb0969fabcd32c0b6e1710Owen Anderson source: 'cache', 63cfa6ec92e61a1ab040c2b79db5de3a39df732ff6Benjamin Kramer id: mmapDumpId, 64cfa6ec92e61a1ab040c2b79db5de3a39df732ff6Benjamin Kramer ruleset: $('#prof-ruleset').val()}); 659945826d50e46212234a6297fda88e2de50f48a6Chris Lattner}; 669945826d50e46212234a6297fda88e2de50f48a6Chris Lattner 679945826d50e46212234a6297fda88e2de50f48a6Chris Lattnerthis.profileArchivedMmaps = function(archiveName, snapshots) { 68c34ebf65af0139eaf5cb0969fabcd32c0b6e1710Owen Anderson // Creates a profile using the data from the storage. 69b663d76f866d03756dd563d246d9bdaabca4ca33Alkis Evlogimenos webservice.ajaxRequest('/profile/create', // This is a POST request. 70 this.onProfileAjaxResponse_.bind(this), 71 null, // use the default error handler. 72 {type: 'mmap', 73 source: 'archive', 74 archive: archiveName, 75 snapshots: snapshots, 76 ruleset: $('#prof-ruleset').val()}); 77}; 78 79this.onProfileAjaxResponse_ = function(data) { 80 // This AJAX response contains a summary of the profile requested via the 81 // /profile endpoint, which consists of: 82 // - The number of snapshots (and their corresponding time) in an array. 83 // e.g., [0, 3 ,6] indicates that the profile contains three snapshots taken 84 // respectively at T=0, T=3 and T=6 sec. 85 // - A list of profile metrics, e.g., ['RSS', 'P. Dirty'] indicates that every 86 // node in the result tree is a 2-tuple. 87 // After this response, the concrete data for the charts can be fetched using 88 // the /ajax/profile/{ID}/tree and /ajax/profile/{ID}/time_serie endpoints. 89 this.profileId_ = data.id; 90 this.times_ = data.times; // An array of integers. 91 this.metrics_ = data.metrics; // An array of strings. 92 this.curBucket_ = data.rootBucket; // URI of the bucket, e.g., Total/Libs/. 93 this.curTime_ = data.times[0]; 94 this.curMetric_ = 0; 95 96 // Populate the "metrics" select box. 97 $('#prof-metric').empty(); 98 this.metrics_.forEach(function(metric) { 99 $('#prof-metric').append($('<option/>').text(metric)); 100 }, this); 101 102 // Setup the bounds of the snapshots slider. 103 $('#prof-time').slider('option', 'max', this.times_.length - 1); 104 105 // Fetch the actual chart data (via /profile/{ID}/...) and redraw the charts. 106 this.updateCharts(); 107}; 108 109this.onUiParamsChange_ = function() { 110 // Triggered whenever any of the UI params (the metric select, the snapshot 111 // slider or the selected bucket in the tree) changes. 112 this.curMetric_ = $('#prof-metric').prop('selectedIndex'); 113 this.curTime_ = this.times_[$('#prof-time').slider('value')]; 114 $('#prof-time_label').text(this.curTime_); 115 var selBucket = this.treeChart_.getSelection(); 116 if (selBucket.length) 117 this.curBucket_ = this.treeData_.getValue(selBucket[0].row, 0); 118 this.updateCharts(); 119}; 120 121this.updateCharts = function() { 122 if (!this.profileId_) 123 return; 124 125 var profileUri = '/profile/' + this.profileId_; 126 webservice.ajaxRequest( 127 profileUri +'/tree/' + this.curMetric_ + '/' + this.curTime_, 128 this.onTreeAjaxResponse_.bind(this)); 129 webservice.ajaxRequest( 130 profileUri +'/time_serie/' + this.curMetric_ + '/' + this.curBucket_, 131 this.onTimeSerieAjaxResponse_.bind(this)); 132}; 133 134this.onTreeAjaxResponse_ = function(data) { 135 this.treeData_ = new google.visualization.DataTable(data); 136 this.redrawTree_(); 137}; 138 139this.onTimeSerieAjaxResponse_ = function(data) { 140 this.timeSeriesData_ = new google.visualization.DataTable(data); 141 this.redrawTimeSerie_(); 142}; 143 144this.redrawTree_ = function() { 145 // isRedrawing_ is used here to break the avalanche chain that would be caused 146 // by redraw changing the node selection, triggering in turn another redraw. 147 if (!this.treeData_ || this.isRedrawing_) 148 return; 149 150 this.isRedrawing_ = true; 151 var savedSelection = this.treeChart_.getSelection(); 152 this.treeChart_.draw(this.treeData_, {allowHtml: true}); 153 154 // "If we want things to stay as they are, things will have to change." 155 // (work around GChart bug, as if we didn't have enough problems on our own). 156 this.treeChart_.setSelection([{row: null, column: null}]); 157 this.treeChart_.setSelection(savedSelection); 158 this.isRedrawing_ = false; 159}; 160 161this.redrawTimeSerie_ = function() { 162 if (!this.timeSeriesData_) 163 return; 164 165 var metric = this.metrics_[this.curMetric_]; 166 this.timeSeriesChart_.draw(this.timeSeriesData_, { 167 title: metric + ' over time for ' + this.curBucket_, 168 isStacked: true, 169 hAxis: {title: 'Time [sec.]'}, 170 vAxis: {title: this.metrics_[this.curMetric_] + ' [KB]'}}); 171}; 172 173this.redraw = function() { 174 this.redrawTree_(); 175 this.redrawTimeSerie_(); 176}; 177 178$(document).ready(this.onDomReady_.bind(this)); 179 180})();