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 base.ui.decorate(this.overlay_, tracing.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_categories) { 94 if (this.tracingEnabled_) 95 throw new Error('Tracing already begun.'); 96 97 this.stopButton_.hidden = false; 98 this.statusDiv_.textContent = 'Tracing active.'; 99 this.overlay_.visible = true; 100 this.overlay_.defaultClickShouldClose = false; 101 102 this.tracingEnabled_ = true; 103 104 console.log('Beginning to trace...'); 105 this.statusDiv_.textContent = 'Tracing active.'; 106 107 this.traceEvents_ = []; 108 this.systemTraceEvents_ = []; 109 this.sendFn_( 110 'beginTracing', 111 [ 112 opt_systemTracingEnabled || false, 113 opt_trace_categories || '-test_*' 114 ] 115 ); 116 this.beginRequestBufferPercentFull_(); 117 118 var e = new base.Event('traceBegun'); 119 e.events = this.traceEvents_; 120 this.dispatchEvent(e); 121 122 e = new base.Event('traceEventsChanged'); 123 e.numEvents = this.traceEvents_.length; 124 this.dispatchEvent(e); 125 126 window.addEventListener('keypress', this.onKeypressBoundToThis_); 127 window.addEventListener('keydown', this.onKeydownBoundToThis_); 128 }, 129 130 onKeydown_: function(e) { 131 if (e.keyCode == 27) { 132 this.endTracing(); 133 } 134 }, 135 136 onKeypress_: function(e) { 137 if (e.keyIdentifier == 'Enter') { 138 this.endTracing(); 139 } 140 }, 141 142 /** 143 * Called from gpu c++ code when ClientInfo is updated. 144 */ 145 onClientInfoUpdate: function(clientInfo) { 146 this.clientInfo_ = clientInfo; 147 }, 148 149 /** 150 * Called from gpu c++ code when GPU Info is updated. 151 */ 152 onGpuInfoUpdate: function(gpuInfo) { 153 this.gpuInfo_ = gpuInfo; 154 }, 155 156 /** 157 * Checks whether tracing is enabled 158 */ 159 get isTracingEnabled() { 160 return this.tracingEnabled_; 161 }, 162 163 /** 164 * Gets the currently traced events. If tracing is active, then 165 * this can change on the fly. 166 */ 167 get traceEvents() { 168 return this.traceEvents_; 169 }, 170 171 /** 172 * Called by tracing c++ code when new trace data arrives. 173 */ 174 onTraceDataCollected: function(events) { 175 this.statusDiv_.textContent = 'Processing trace...'; 176 this.traceEvents_.push.apply(this.traceEvents_, events); 177 }, 178 179 /** 180 * Called to finish tracing and update all views. 181 */ 182 endTracing: function() { 183 if (!this.tracingEnabled_) throw new Error('Tracing not begun.'); 184 if (this.tracingEnding_) return; 185 this.tracingEnding_ = true; 186 187 this.statusDiv_.textContent = 'Ending trace...'; 188 console.log('Finishing trace'); 189 this.statusDiv_.textContent = 'Downloading trace data...'; 190 this.stopButton_.hidden = true; 191 // delay sending endTracingAsync until we get a chance to 192 // update the screen... 193 var that = this; 194 window.setTimeout(function() { 195 that.sendFn_('endTracingAsync'); 196 }, 100); 197 }, 198 199 /** 200 * Called by the browser when all processes complete tracing. 201 */ 202 onEndTracingComplete: function() { 203 window.removeEventListener('keydown', this.onKeydownBoundToThis_); 204 window.removeEventListener('keypress', this.onKeypressBoundToThis_); 205 this.overlay_.visible = false; 206 this.tracingEnabled_ = false; 207 this.tracingEnding_ = false; 208 console.log('onEndTracingComplete p1 with ' + 209 this.traceEvents_.length + ' events.'); 210 var e = new base.Event('traceEnded'); 211 e.events = this.traceEvents_; 212 this.dispatchEvent(e); 213 }, 214 215 /** 216 * Called by tracing c++ code when new system trace data arrives. 217 */ 218 onSystemTraceDataCollected: function(events) { 219 console.log('onSystemTraceDataCollected with ' + 220 events.length + ' chars of data.'); 221 this.systemTraceEvents_ = events; 222 }, 223 224 /** 225 * Gets the currentl system trace events. If tracing is active, then 226 * this can change on the fly. 227 */ 228 get systemTraceEvents() { 229 return this.systemTraceEvents_; 230 }, 231 232 /** 233 * Tells browser to put up a load dialog and load the trace file 234 */ 235 beginLoadTraceFile: function() { 236 this.sendFn_('loadTraceFile'); 237 }, 238 239 /** 240 * Called by the browser when a trace file is loaded. 241 */ 242 onLoadTraceFileComplete: function(data) { 243 if (data.traceEvents) { 244 this.traceEvents_ = data.traceEvents; 245 } else { // path for loading traces saved without metadata 246 if (!data.length) 247 console.log('Expected an array when loading the trace file'); 248 else 249 this.traceEvents_ = data; 250 } 251 252 if (data.systemTraceEvents) 253 this.systemTraceEvents_ = data.systemTraceEvents; 254 else 255 this.systemTraceEvents_ = []; 256 257 var e = new base.Event('loadTraceFileComplete'); 258 e.events = this.traceEvents_; 259 this.dispatchEvent(e); 260 }, 261 262 /** 263 * Called by the browser when loading a trace file was canceled. 264 */ 265 onLoadTraceFileCanceled: function() { 266 base.dispatchSimpleEvent(this, 'loadTraceFileCanceled'); 267 }, 268 269 /** 270 * Tells browser to put up a save dialog and save the trace file 271 */ 272 beginSaveTraceFile: function(traceEvents, systemTraceEvents) { 273 var data = { 274 traceEvents: this.traceEvents_, 275 systemTraceEvents: this.systemTraceEvents_, 276 clientInfo: this.clientInfo_, 277 gpuInfo: this.gpuInfo_ 278 }; 279 this.sendFn_('saveTraceFile', [JSON.stringify(data)]); 280 }, 281 282 /** 283 * Called by the browser when a trace file is saveed. 284 */ 285 onSaveTraceFileComplete: function() { 286 base.dispatchSimpleEvent(this, 'saveTraceFileComplete'); 287 }, 288 289 /** 290 * Called by the browser when saving a trace file was canceled. 291 */ 292 onSaveTraceFileCanceled: function() { 293 base.dispatchSimpleEvent(this, 'saveTraceFileCanceled'); 294 } 295 }; 296 return { 297 TracingController: TracingController 298 }; 299 }); 300