blob: 6d338ddf19aedb3c3c74d7ac06d457923708aa75 [file] [log] [blame]
// 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';
/**
* @fileoverview State and UI for trace data collection.
*/
base.requireStylesheet('about_tracing.tracing_controller');
base.require('base.properties');
base.require('base.events');
base.require('ui.overlay');
base.exportTo('about_tracing', function() {
/**
* The tracing controller is responsible for talking to tracing_ui.cc in
* chrome
* @constructor
* @param {function(String, opt_Array.<String>} Function to be used to send
* data to chrome.
*/
function TracingController(sendFn) {
this.sendFn_ = sendFn;
this.overlay_ = new ui.Overlay();
this.overlay_.className = 'tracing-overlay';
this.statusDiv_ = document.createElement('div');
this.overlay_.appendChild(this.statusDiv_);
this.bufferPercentDiv_ = document.createElement('div');
this.overlay_.appendChild(this.bufferPercentDiv_);
this.stopButton_ = document.createElement('button');
this.stopButton_.onclick = this.endTracing.bind(this);
this.stopButton_.textContent = 'Stop tracing';
this.overlay_.appendChild(this.stopButton_);
this.traceEventData_ = undefined;
this.systemTraceEvents_ = undefined;
this.onKeydown_ = this.onKeydown_.bind(this);
this.onKeypress_ = this.onKeypress_.bind(this);
this.supportsSystemTracing_ = base.isChromeOS;
if (this.sendFn_)
this.sendFn_('tracingControllerInitialized');
}
TracingController.prototype = {
__proto__: base.EventTarget.prototype,
gpuInfo_: undefined,
clientInfo_: undefined,
tracingEnabled_: false,
tracingEnding_: false,
systemTraceDataFilename_: undefined,
get supportsSystemTracing() {
return this.supportsSystemTracing_;
},
onRequestBufferPercentFullComplete: function(percent_full) {
if (!this.overlay_.visible)
return;
window.setTimeout(this.beginRequestBufferPercentFull_.bind(this), 500);
var newText = 'Buffer usage: ' +
Math.round(100 * percent_full) + '%';
if (this.bufferPercentDiv_.textContent != newText)
this.bufferPercentDiv_.textContent = newText;
},
/**
* Begin requesting the buffer fullness
*/
beginRequestBufferPercentFull_: function() {
this.sendFn_('beginRequestBufferPercentFull');
},
/**
* Called by info_view to empty the trace buffer
*
* |opt_trace_categories| is a comma-delimited list of category wildcards.
* A category can have an optional '-' prefix to make it an excluded
* category. All the same rules apply above, so for example, having both
* included and excluded categories in the same list would not be
* supported.
*
* Example: beginTracing("test_MyTest*");
* Example: beginTracing("test_MyTest*,test_OtherStuff");
* Example: beginTracing("-excluded_category1,-excluded_category2");
*/
beginTracing: function(opt_systemTracingEnabled, opt_trace_continuous,
opt_enableSampling, opt_trace_categories) {
if (this.tracingEnabled_)
throw new Error('Tracing already begun.');
this.stopButton_.hidden = false;
this.statusDiv_.textContent = 'Tracing active.';
this.overlay_.obeyCloseEvents = false;
this.overlay_.visible = true;
this.tracingEnabled_ = true;
console.log('Beginning to trace...');
this.statusDiv_.textContent = 'Tracing active.';
var trace_options = [];
trace_options.push(opt_trace_continuous ? 'record-continuously' :
'record-until-full');
if (opt_enableSampling)
trace_options.push('enable-sampling');
this.traceEventData_ = undefined;
this.systemTraceEvents_ = undefined;
this.sendFn_(
'beginTracing',
[
opt_systemTracingEnabled || false,
opt_trace_categories || '-test_*',
trace_options.join(',')
]
);
this.beginRequestBufferPercentFull_();
window.addEventListener('keypress', this.onKeypress_);
window.addEventListener('keydown', this.onKeydown_);
},
onKeydown_: function(e) {
if (e.keyCode == 27) {
this.endTracing();
}
},
onKeypress_: function(e) {
if (e.keyIdentifier == 'Enter') {
this.endTracing();
}
},
/**
* Called from gpu c++ code when ClientInfo is updated.
*/
onClientInfoUpdate: function(clientInfo) {
this.clientInfo_ = clientInfo;
},
/**
* Called from gpu c++ code when GPU Info is updated.
*/
onGpuInfoUpdate: function(gpuInfo) {
this.gpuInfo_ = gpuInfo;
},
/**
* Checks whether tracing is enabled
*/
get isTracingEnabled() {
return this.tracingEnabled_;
},
/**
* Gets the currently traced events. If tracing is active, then
* this can change on the fly.
*/
get traceEventData() {
return this.traceEventData_;
},
/**
* Called to finish tracing and update all views.
*/
endTracing: function() {
if (!this.tracingEnabled_) throw new Error('Tracing not begun.');
if (this.tracingEnding_) return;
this.tracingEnding_ = true;
this.statusDiv_.textContent = 'Ending trace...';
console.log('Finishing trace');
this.statusDiv_.textContent = 'Downloading trace data...';
this.stopButton_.hidden = true;
// delay sending endTracingAsync until we get a chance to
// update the screen...
var that = this;
window.setTimeout(function() {
that.sendFn_('endTracingAsync');
}, 100);
},
/**
* Called by the browser when all processes complete tracing.
*/
onEndTracingComplete: function(traceDataString) {
window.removeEventListener('keydown', this.onKeydown_);
window.removeEventListener('keypress', this.onKeypress_);
this.overlay_.visible = false;
this.tracingEnabled_ = false;
this.tracingEnding_ = false;
if (traceDataString[traceDataString.length - 1] == ',')
traceDataString = traceDataString.substr(0, traceDataString.length - 1);
if (traceDataString[0] != '[')
traceDataString = '[' + traceDataString;
if (traceDataString[traceDataString.length - 1] != ']')
traceDataString = traceDataString + ']';
this.traceEventData_ = traceDataString;
console.log('onEndTracingComplete p1 with ' +
this.traceEventData_.length + ' bytes of data.');
var e = new base.Event('traceEnded');
this.dispatchEvent(e);
},
collectCategories: function() {
this.sendFn_('getKnownCategories');
},
onKnownCategoriesCollected: function(categories) {
var e = new base.Event('categoriesCollected');
e.categories = categories;
this.dispatchEvent(e);
},
/**
* Called by tracing c++ code when new system trace data arrives.
*/
onSystemTraceDataCollected: function(events) {
console.log('onSystemTraceDataCollected with ' +
events.length + ' chars of data.');
this.systemTraceEvents_ = events;
},
/**
* Gets the currentl system trace events. If tracing is active, then
* this can change on the fly.
*/
get systemTraceEvents() {
return this.systemTraceEvents_;
},
/**
* Tells browser to put up a load dialog and load the trace file
*/
beginLoadTraceFile: function() {
this.sendFn_('loadTraceFile');
},
/**
* Called by the browser when a trace file is loaded.
*/
onLoadTraceFileComplete: function(traceDataString, opt_filename) {
this.traceEventData_ = traceDataString;
this.systemTraceEvents_ = undefined;
var e = new base.Event('loadTraceFileComplete');
e.filename = opt_filename || '';
this.dispatchEvent(e);
},
/**
* Called by the browser when loading a trace file was canceled.
*/
onLoadTraceFileCanceled: function() {
base.dispatchSimpleEvent(this, 'loadTraceFileCanceled');
},
/**
* Tells browser to put up a save dialog and save the trace file
*/
beginSaveTraceFile: function() {
// this.traceEventData_ is already in JSON form, but now need to insert it
// into a data structure containing metadata about the recording. To do
// this "right," we should parse the traceEventData_, make the new data
// structure and then JSONize the lot. But, the traceEventData_ is huge so
// parsing it and stringifying it again is going to consume time and
// memory.
//
// Instead, we make the new data strcture with a placeholder string,
// JSONify it, then replace the placeholder string with the
// traceEventData_.
var data = {
traceEvents: '__TRACE_EVENT_PLACEHOLDER__',
systemTraceEvents: this.systemTraceEvents_,
clientInfo: this.clientInfo_,
gpuInfo: this.gpuInfo_
};
var dataAsString = JSON.stringify(data);
dataAsString = dataAsString.replace('"__TRACE_EVENT_PLACEHOLDER__"',
this.traceEventData_);
this.sendFn_('saveTraceFile', [dataAsString]);
},
/**
* Called by the browser when a trace file is saveed.
*/
onSaveTraceFileComplete: function() {
base.dispatchSimpleEvent(this, 'saveTraceFileComplete');
},
/**
* Called by the browser when saving a trace file was canceled.
*/
onSaveTraceFileCanceled: function() {
base.dispatchSimpleEvent(this, 'saveTraceFileCanceled');
}
};
return {
TracingController: TracingController
};
});