• 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'use strict';
6
7/**
8 * @fileoverview State and UI for trace data collection.
9 */
10base.requireStylesheet('about_tracing.tracing_controller');
11
12base.require('base.properties');
13base.require('base.events');
14base.require('ui.overlay');
15
16base.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