• 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    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