1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 6 /** 7 * @fileoverview State and UI for trace data collection. 8 */ 9 base.requireStylesheet('tracing_controller'); 10 base.require('event_target'); 11 base.exportTo('tracing', function() { 12 13 /** 14 * The tracing controller is responsible for talking to tracing_ui.cc in 15 * chrome 16 * @constructor 17 * @param {function(String, opt_Array.<String>} Function to be used to send 18 * data to chrome. 19 */ 20 function TracingController(sendFn) { 21 this.sendFn_ = sendFn; 22 this.overlay_ = document.createElement('div'); 23 this.overlay_.className = 'tracing-overlay'; 24 25 tracing.ui.decorate(this.overlay_, tracing.ui.Overlay); 26 27 this.statusDiv_ = document.createElement('div'); 28 this.overlay_.appendChild(this.statusDiv_); 29 30 this.bufferPercentDiv_ = document.createElement('div'); 31 this.overlay_.appendChild(this.bufferPercentDiv_); 32 33 this.stopButton_ = document.createElement('button'); 34 this.stopButton_.onclick = this.endTracing.bind(this); 35 this.stopButton_.textContent = 'Stop tracing'; 36 this.overlay_.appendChild(this.stopButton_); 37 38 this.traceEvents_ = []; 39 this.systemTraceEvents_ = []; 40 41 this.onKeydownBoundToThis_ = this.onKeydown_.bind(this); 42 this.onKeypressBoundToThis_ = this.onKeypress_.bind(this); 43 44 this.supportsSystemTracing_ = base.isChromeOS; 45 46 if (this.sendFn_) 47 this.sendFn_('tracingControllerInitialized'); 48 } 49 50 TracingController.prototype = { 51 __proto__: base.EventTarget.prototype, 52 53 gpuInfo_: undefined, 54 clientInfo_: undefined, 55 tracingEnabled_: false, 56 tracingEnding_: false, 57 systemTraceDataFilename_: undefined, 58 59 get supportsSystemTracing() { 60 return this.supportsSystemTracing_; 61 }, 62 63 onRequestBufferPercentFullComplete: function(percent_full) { 64 if (!this.overlay_.visible) 65 return; 66 67 window.setTimeout(this.beginRequestBufferPercentFull_.bind(this), 250); 68 69 this.bufferPercentDiv_.textContent = 'Buffer usage: ' + 70 Math.round(100 * percent_full) + '%'; 71 }, 72 73 /** 74 * Begin requesting the buffer fullness 75 */ 76 beginRequestBufferPercentFull_: function() { 77 this.sendFn_('beginRequestBufferPercentFull'); 78 }, 79 80 /** 81 * Called by info_view to empty the trace buffer 82 * 83 * |opt_trace_categories| is a comma-delimited list of category wildcards. 84 * A category can have an optional '-' prefix to make it an excluded 85 * category. All the same rules apply above, so for example, having both 86 * included and excluded categories in the same list would not be 87 * supported. 88 * 89 * Example: beginTracing("test_MyTest*"); 90 * Example: beginTracing("test_MyTest*,test_OtherStuff"); 91 * Example: beginTracing("-excluded_category1,-excluded_category2"); 92 */ 93 beginTracing: function(opt_systemTracingEnabled, opt_trace_continuous, 94 opt_trace_categories) { 95 if (this.tracingEnabled_) 96 throw new Error('Tracing already begun.'); 97 98 this.stopButton_.hidden = false; 99 this.statusDiv_.textContent = 'Tracing active.'; 100 this.overlay_.visible = true; 101 this.overlay_.defaultClickShouldClose = false; 102 103 this.tracingEnabled_ = true; 104 105 console.log('Beginning to trace...'); 106 this.statusDiv_.textContent = 'Tracing active.'; 107 108 var trace_options = (opt_trace_continuous ? 'record-continuously' : 109 'record-until-full'); 110 111 this.traceEvents_ = []; 112 this.systemTraceEvents_ = []; 113 this.sendFn_( 114 'beginTracing', 115 [ 116 opt_systemTracingEnabled || false, 117 opt_trace_categories || '-test_*', 118 trace_options 119 ] 120 ); 121 this.beginRequestBufferPercentFull_(); 122 123 var e = new base.Event('traceBegun'); 124 e.events = this.traceEvents_; 125 this.dispatchEvent(e); 126 127 e = new base.Event('traceEventsChanged'); 128 e.numEvents = this.traceEvents_.length; 129 this.dispatchEvent(e); 130 131 window.addEventListener('keypress', this.onKeypressBoundToThis_); 132 window.addEventListener('keydown', this.onKeydownBoundToThis_); 133 }, 134 135 onKeydown_: function(e) { 136 if (e.keyCode == 27) { 137 this.endTracing(); 138 } 139 }, 140 141 onKeypress_: function(e) { 142 if (e.keyIdentifier == 'Enter') { 143 this.endTracing(); 144 } 145 }, 146 147 /** 148 * Called from gpu c++ code when ClientInfo is updated. 149 */ 150 onClientInfoUpdate: function(clientInfo) { 151 this.clientInfo_ = clientInfo; 152 }, 153 154 /** 155 * Called from gpu c++ code when GPU Info is updated. 156 */ 157 onGpuInfoUpdate: function(gpuInfo) { 158 this.gpuInfo_ = gpuInfo; 159 }, 160 161 /** 162 * Checks whether tracing is enabled 163 */ 164 get isTracingEnabled() { 165 return this.tracingEnabled_; 166 }, 167 168 /** 169 * Gets the currently traced events. If tracing is active, then 170 * this can change on the fly. 171 */ 172 get traceEvents() { 173 return this.traceEvents_; 174 }, 175 176 /** 177 * Called by tracing c++ code when new trace data arrives. 178 */ 179 onTraceDataCollected: function(events) { 180 this.statusDiv_.textContent = 'Processing trace...'; 181 this.traceEvents_.push.apply(this.traceEvents_, events); 182 }, 183 184 /** 185 * Called to finish tracing and update all views. 186 */ 187 endTracing: function() { 188 if (!this.tracingEnabled_) throw new Error('Tracing not begun.'); 189 if (this.tracingEnding_) return; 190 this.tracingEnding_ = true; 191 192 this.statusDiv_.textContent = 'Ending trace...'; 193 console.log('Finishing trace'); 194 this.statusDiv_.textContent = 'Downloading trace data...'; 195 this.stopButton_.hidden = true; 196 // delay sending endTracingAsync until we get a chance to 197 // update the screen... 198 var that = this; 199 window.setTimeout(function() { 200 that.sendFn_('endTracingAsync'); 201 }, 100); 202 }, 203 204 /** 205 * Called by the browser when all processes complete tracing. 206 */ 207 onEndTracingComplete: function() { 208 window.removeEventListener('keydown', this.onKeydownBoundToThis_); 209 window.removeEventListener('keypress', this.onKeypressBoundToThis_); 210 this.overlay_.visible = false; 211 this.tracingEnabled_ = false; 212 this.tracingEnding_ = false; 213 console.log('onEndTracingComplete p1 with ' + 214 this.traceEvents_.length + ' events.'); 215 var e = new base.Event('traceEnded'); 216 e.events = this.traceEvents_; 217 this.dispatchEvent(e); 218 }, 219 220 collectCategories: function() { 221 this.sendFn_('getKnownCategories'); 222 }, 223 224 onKnownCategoriesCollected: function(categories) { 225 var e = new base.Event('categoriesCollected'); 226 e.categories = categories; 227 this.dispatchEvent(e); 228 }, 229 230 231 /** 232 * Called by tracing c++ code when new system trace data arrives. 233 */ 234 onSystemTraceDataCollected: function(events) { 235 console.log('onSystemTraceDataCollected with ' + 236 events.length + ' chars of data.'); 237 this.systemTraceEvents_ = events; 238 }, 239 240 /** 241 * Gets the currentl system trace events. If tracing is active, then 242 * this can change on the fly. 243 */ 244 get systemTraceEvents() { 245 return this.systemTraceEvents_; 246 }, 247 248 /** 249 * Tells browser to put up a load dialog and load the trace file 250 */ 251 beginLoadTraceFile: function() { 252 this.sendFn_('loadTraceFile'); 253 }, 254 255 /** 256 * Called by the browser when a trace file is loaded. 257 */ 258 onLoadTraceFileComplete: function(data) { 259 if (data.traceEvents) { 260 this.traceEvents_ = data.traceEvents; 261 } else { // path for loading traces saved without metadata 262 if (!data.length) 263 console.log('Expected an array when loading the trace file'); 264 else 265 this.traceEvents_ = data; 266 } 267 268 if (data.systemTraceEvents) 269 this.systemTraceEvents_ = data.systemTraceEvents; 270 else 271 this.systemTraceEvents_ = []; 272 273 var e = new base.Event('loadTraceFileComplete'); 274 e.events = this.traceEvents_; 275 this.dispatchEvent(e); 276 }, 277 278 /** 279 * Called by the browser when loading a trace file was canceled. 280 */ 281 onLoadTraceFileCanceled: function() { 282 base.dispatchSimpleEvent(this, 'loadTraceFileCanceled'); 283 }, 284 285 /** 286 * Tells browser to put up a save dialog and save the trace file 287 */ 288 beginSaveTraceFile: function(traceEvents, systemTraceEvents) { 289 var data = { 290 traceEvents: this.traceEvents_, 291 systemTraceEvents: this.systemTraceEvents_, 292 clientInfo: this.clientInfo_, 293 gpuInfo: this.gpuInfo_ 294 }; 295 this.sendFn_('saveTraceFile', [JSON.stringify(data)]); 296 }, 297 298 /** 299 * Called by the browser when a trace file is saveed. 300 */ 301 onSaveTraceFileComplete: function() { 302 base.dispatchSimpleEvent(this, 'saveTraceFileComplete'); 303 }, 304 305 /** 306 * Called by the browser when saving a trace file was canceled. 307 */ 308 onSaveTraceFileCanceled: function() { 309 base.dispatchSimpleEvent(this, 'saveTraceFileCanceled'); 310 } 311 }; 312 return { 313 TracingController: TracingController 314 }; 315 }); 316