• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
9base.requireStylesheet('tracing_controller');
10base.require('event_target');
11base.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