Home | History | Annotate | Download | only in src
      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