Home | History | Annotate | Download | only in about_tracing
      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 'use strict';
      6 
      7 /**
      8  * @fileoverview State and UI for trace data collection.
      9  */
     10 base.requireStylesheet('about_tracing.tracing_controller');
     11 
     12 base.require('base.properties');
     13 base.require('base.events');
     14 base.require('ui.overlay');
     15 
     16 base.exportTo('about_tracing', function() {
     17 
     18   /**
     19    * The tracing controller is responsible for talking to tracing_ui.cc in
     20    * chrome
     21    * @constructor
     22    * @param {function(String, opt_Array.<String>} Function to be used to send
     23    * data to chrome.
     24    */
     25   function TracingController(sendFn) {
     26     this.sendFn_ = sendFn;
     27     this.overlay_ = new ui.Overlay();
     28     this.overlay_.className = 'tracing-overlay';
     29 
     30     this.statusDiv_ = document.createElement('div');
     31     this.overlay_.appendChild(this.statusDiv_);
     32 
     33     this.bufferPercentDiv_ = document.createElement('div');
     34     this.overlay_.appendChild(this.bufferPercentDiv_);
     35 
     36     this.stopButton_ = document.createElement('button');
     37     this.stopButton_.onclick = this.endTracing.bind(this);
     38     this.stopButton_.textContent = 'Stop tracing';
     39     this.overlay_.appendChild(this.stopButton_);
     40 
     41     this.traceEventData_ = undefined;
     42     this.systemTraceEvents_ = undefined;
     43 
     44     this.onKeydown_ = this.onKeydown_.bind(this);
     45     this.onKeypress_ = this.onKeypress_.bind(this);
     46 
     47     this.supportsSystemTracing_ = base.isChromeOS;
     48 
     49     if (this.sendFn_)
     50       this.sendFn_('tracingControllerInitialized');
     51   }
     52 
     53   TracingController.prototype = {
     54     __proto__: base.EventTarget.prototype,
     55 
     56     gpuInfo_: undefined,
     57     clientInfo_: undefined,
     58     tracingEnabled_: false,
     59     tracingEnding_: false,
     60     systemTraceDataFilename_: undefined,
     61 
     62     get supportsSystemTracing() {
     63       return this.supportsSystemTracing_;
     64     },
     65 
     66     onRequestBufferPercentFullComplete: function(percent_full) {
     67       if (!this.overlay_.visible)
     68         return;
     69 
     70       window.setTimeout(this.beginRequestBufferPercentFull_.bind(this), 500);
     71 
     72       var newText = 'Buffer usage: ' +
     73           Math.round(100 * percent_full) + '%';
     74       if (this.bufferPercentDiv_.textContent != newText)
     75         this.bufferPercentDiv_.textContent = newText;
     76     },
     77 
     78     /**
     79      * Begin requesting the buffer fullness
     80      */
     81     beginRequestBufferPercentFull_: function() {
     82       this.sendFn_('beginRequestBufferPercentFull');
     83     },
     84 
     85     /**
     86      * Called by info_view to empty the trace buffer
     87      *
     88      * |opt_trace_categories| is a comma-delimited list of category wildcards.
     89      * A category can have an optional '-' prefix to make it an excluded
     90      * category.  All the same rules apply above, so for example, having both
     91      * included and excluded categories in the same list would not be
     92      * supported.
     93      *
     94      * Example: beginTracing("test_MyTest*");
     95      * Example: beginTracing("test_MyTest*,test_OtherStuff");
     96      * Example: beginTracing("-excluded_category1,-excluded_category2");
     97      */
     98     beginTracing: function(opt_systemTracingEnabled, opt_trace_continuous,
     99                            opt_enableSampling, opt_trace_categories) {
    100       if (this.tracingEnabled_)
    101         throw new Error('Tracing already begun.');
    102 
    103       this.stopButton_.hidden = false;
    104       this.statusDiv_.textContent = 'Tracing active.';
    105       this.overlay_.obeyCloseEvents = false;
    106       this.overlay_.visible = true;
    107 
    108       this.tracingEnabled_ = true;
    109 
    110       console.log('Beginning to trace...');
    111       this.statusDiv_.textContent = 'Tracing active.';
    112 
    113       var trace_options = [];
    114       trace_options.push(opt_trace_continuous ? 'record-continuously' :
    115                                                 'record-until-full');
    116       if (opt_enableSampling)
    117         trace_options.push('enable-sampling');
    118 
    119       this.traceEventData_ = undefined;
    120       this.systemTraceEvents_ = undefined;
    121       this.sendFn_(
    122           'beginTracing',
    123           [
    124            opt_systemTracingEnabled || false,
    125            opt_trace_categories || '-test_*',
    126            trace_options.join(',')
    127           ]
    128       );
    129       this.beginRequestBufferPercentFull_();
    130 
    131       window.addEventListener('keypress', this.onKeypress_);
    132       window.addEventListener('keydown', this.onKeydown_);
    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 traceEventData() {
    173       return this.traceEventData_;
    174     },
    175 
    176     /**
    177      * Called to finish tracing and update all views.
    178      */
    179     endTracing: function() {
    180       if (!this.tracingEnabled_) throw new Error('Tracing not begun.');
    181       if (this.tracingEnding_) return;
    182       this.tracingEnding_ = true;
    183 
    184       this.statusDiv_.textContent = 'Ending trace...';
    185       console.log('Finishing trace');
    186       this.statusDiv_.textContent = 'Downloading trace data...';
    187       this.stopButton_.hidden = true;
    188       // delay sending endTracingAsync until we get a chance to
    189       // update the screen...
    190       var that = this;
    191       window.setTimeout(function() {
    192         that.sendFn_('endTracingAsync');
    193       }, 100);
    194     },
    195 
    196     /**
    197      * Called by the browser when all processes complete tracing.
    198      */
    199     onEndTracingComplete: function(traceDataString) {
    200       window.removeEventListener('keydown', this.onKeydown_);
    201       window.removeEventListener('keypress', this.onKeypress_);
    202       this.overlay_.visible = false;
    203       this.tracingEnabled_ = false;
    204       this.tracingEnding_ = false;
    205 
    206       if (traceDataString[traceDataString.length - 1] == ',')
    207         traceDataString = traceDataString.substr(0, traceDataString.length - 1);
    208       if (traceDataString[0] != '[')
    209         traceDataString = '[' + traceDataString;
    210       if (traceDataString[traceDataString.length - 1] != ']')
    211         traceDataString = traceDataString + ']';
    212 
    213       this.traceEventData_ = traceDataString;
    214 
    215       console.log('onEndTracingComplete p1 with ' +
    216                   this.traceEventData_.length + ' bytes of data.');
    217       var e = new base.Event('traceEnded');
    218       this.dispatchEvent(e);
    219     },
    220 
    221     collectCategories: function() {
    222       this.sendFn_('getKnownCategories');
    223     },
    224 
    225     onKnownCategoriesCollected: function(categories) {
    226       var e = new base.Event('categoriesCollected');
    227       e.categories = categories;
    228       this.dispatchEvent(e);
    229     },
    230 
    231 
    232     /**
    233      * Called by tracing c++ code when new system trace data arrives.
    234      */
    235     onSystemTraceDataCollected: function(events) {
    236       console.log('onSystemTraceDataCollected with ' +
    237                   events.length + ' chars of data.');
    238       this.systemTraceEvents_ = events;
    239     },
    240 
    241     /**
    242      * Gets the currentl system trace events. If tracing is active, then
    243      * this can change on the fly.
    244      */
    245     get systemTraceEvents() {
    246       return this.systemTraceEvents_;
    247     },
    248 
    249     /**
    250      * Tells browser to put up a load dialog and load the trace file
    251      */
    252     beginLoadTraceFile: function() {
    253       this.sendFn_('loadTraceFile');
    254     },
    255 
    256     /**
    257      * Called by the browser when a trace file is loaded.
    258      */
    259     onLoadTraceFileComplete: function(traceDataString, opt_filename) {
    260       this.traceEventData_ = traceDataString;
    261       this.systemTraceEvents_ = undefined;
    262 
    263       var e = new base.Event('loadTraceFileComplete');
    264       e.filename = opt_filename || '';
    265       this.dispatchEvent(e);
    266     },
    267 
    268     /**
    269      * Called by the browser when loading a trace file was canceled.
    270      */
    271     onLoadTraceFileCanceled: function() {
    272       base.dispatchSimpleEvent(this, 'loadTraceFileCanceled');
    273     },
    274 
    275     /**
    276      * Tells browser to put up a save dialog and save the trace file
    277      */
    278     beginSaveTraceFile: function() {
    279       // this.traceEventData_ is already in JSON form, but now need to insert it
    280       // into a data structure containing metadata about the recording. To do
    281       // this "right," we should parse the traceEventData_, make the new data
    282       // structure and then JSONize the lot. But, the traceEventData_ is huge so
    283       // parsing it and stringifying it again is going to consume time and
    284       // memory.
    285       //
    286       // Instead, we make the new data strcture with a placeholder string,
    287       // JSONify it, then replace the placeholder string with the
    288       // traceEventData_.
    289       var data = {
    290         traceEvents: '__TRACE_EVENT_PLACEHOLDER__',
    291         systemTraceEvents: this.systemTraceEvents_,
    292         clientInfo: this.clientInfo_,
    293         gpuInfo: this.gpuInfo_
    294       };
    295       var dataAsString = JSON.stringify(data);
    296       dataAsString = dataAsString.replace('"__TRACE_EVENT_PLACEHOLDER__"',
    297                                           this.traceEventData_);
    298       this.sendFn_('saveTraceFile', [dataAsString]);
    299     },
    300 
    301     /**
    302      * Called by the browser when a trace file is saveed.
    303      */
    304     onSaveTraceFileComplete: function() {
    305       base.dispatchSimpleEvent(this, 'saveTraceFileComplete');
    306     },
    307 
    308     /**
    309      * Called by the browser when saving a trace file was canceled.
    310      */
    311     onSaveTraceFileCanceled: function() {
    312       base.dispatchSimpleEvent(this, 'saveTraceFileCanceled');
    313     }
    314   };
    315   return {
    316     TracingController: TracingController
    317   };
    318 });
    319