1// Copyright (c) 2011 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 */
9cr.define('gpu', function() {
10
11  function TracingController() {
12    this.overlay_ = document.createElement('div');
13    this.overlay_.className = 'gpu-tracing-overlay';
14
15    cr.ui.decorate(this.overlay_, gpu.Overlay);
16
17    this.statusDiv_ = document.createElement('div');
18    this.overlay_.appendChild(this.statusDiv_);
19
20    this.bufferPercentDiv_ = document.createElement('div');
21    this.overlay_.appendChild(this.bufferPercentDiv_);
22
23    this.stopButton_ = document.createElement('button');
24    this.stopButton_.onclick = this.endTracing.bind(this);
25    this.stopButton_.innerText = 'Stop tracing';
26    this.overlay_.appendChild(this.stopButton_);
27
28    this.traceEvents_ = [];
29
30    if (browserBridge.debugMode) {
31      var tracingControllerTests = document.createElement('script');
32      tracingControllerTests.src =
33          './gpu_internals/tracing_controller_tests.js';
34      document.body.appendChild(tracingControllerTests);
35    }
36
37    this.onKeydownBoundToThis_ = this.onKeydown_.bind(this);
38    this.onKeypressBoundToThis_ = this.onKeypress_.bind(this);
39  }
40
41  TracingController.prototype = {
42    __proto__: cr.EventTarget.prototype,
43
44    tracingEnabled_: false,
45    tracingEnding_: false,
46
47    onRequestBufferPercentFullComplete: function(percent_full) {
48      if (!this.overlay_.visible)
49        return;
50
51      window.setTimeout(this.beginRequestBufferPercentFull_.bind(this), 250);
52
53      this.bufferPercentDiv_.textContent = 'Buffer usage: ' +
54          Math.round(100 * percent_full) + '%';
55    },
56
57    /**
58     * Begin requesting the buffer fullness
59     */
60    beginRequestBufferPercentFull_: function() {
61      chrome.send('beginRequestBufferPercentFull');
62    },
63
64    /**
65     * Called by info_view to empty the trace buffer
66     */
67    beginTracing: function() {
68      if (this.tracingEnabled_)
69        throw Error('Tracing already begun.');
70
71      this.stopButton_.hidden = false;
72      this.statusDiv_.textContent = 'Tracing active.';
73      this.overlay_.visible = true;
74
75      this.tracingEnabled_ = true;
76      console.log('Beginning to trace...');
77      this.statusDiv_.textContent = 'Tracing active.';
78
79      this.traceEvents_ = [];
80      if (!browserBridge.debugMode) {
81        chrome.send('beginTracing');
82        this.beginRequestBufferPercentFull_();
83      }
84
85      this.tracingEnabled_ = true;
86
87      var e = new cr.Event('traceBegun');
88      e.events = this.traceEvents_;
89      this.dispatchEvent(e);
90
91      e = new cr.Event('traceEventsChanged');
92      e.numEvents = this.traceEvents_.length;
93      this.dispatchEvent(e);
94
95      window.addEventListener('keypress', this.onKeypressBoundToThis_);
96      window.addEventListener('keydown', this.onKeydownBoundToThis_);
97
98      // In debug mode, stop tracing automatically
99      if (browserBridge.debugMode)
100        window.setTimeout(this.endTracing.bind(this), 100);
101    },
102
103    onKeydown_: function(e) {
104      if (e.keyCode == 27) {
105        this.endTracing();
106      }
107    },
108
109    onKeypress_: function(e) {
110      if (e.keyIdentifier == 'Enter') {
111        this.endTracing();
112      }
113    },
114    /**
115     * Checks whether tracing is enabled
116     */
117    get isTracingEnabled() {
118      return this.tracingEnabled_;
119    },
120
121    /**
122     * Gets the currently traced events. If tracing is active, then
123     * this can change on the fly.
124     */
125    get traceEvents() {
126      return this.traceEvents_;
127    },
128
129    /**
130     * Callbed by gpu c++ code when new GPU trace data arrives.
131     */
132    onTraceDataCollected: function(events) {
133      this.statusDiv_.textContent = 'Processing trace...';
134      this.traceEvents_.push.apply(this.traceEvents_, events);
135    },
136
137    /**
138     * Called by info_view to finish tracing and update all views.
139     */
140    endTracing: function() {
141      if (!this.tracingEnabled_) throw new Error('Tracing not begun.');
142      if (this.tracingEnding_) return;
143      this.tracingEnding_ = true;
144
145      this.statusDiv_.textContent = 'Ending trace...';
146      console.log('Finishing trace');
147      this.statusDiv_.textContent = 'Downloading trace data...';
148      this.stopButton_.hidden = true;
149      if (!browserBridge.debugMode) {
150        // delay sending endTracingAsync until we get a chance to
151        // update the screen...
152        window.setTimeout(function() {
153          chrome.send('endTracingAsync');
154        }, 100);
155      } else {
156        var events = tracingControllerTestEvents;
157        this.onTraceDataCollected(events);
158        window.setTimeout(this.onEndTracingComplete.bind(this), 250);
159      }
160    },
161
162
163    /**
164     * Called by the browser when all processes complete tracing.
165     */
166    onEndTracingComplete: function() {
167      window.removeEventListener('keydown', this.onKeydownBoundToThis_);
168      window.removeEventListener('keypress', this.onKeypressBoundToThis_);
169      this.overlay_.visible = false;
170      this.tracingEnabled_ = false;
171      this.tracingEnding_ = false;
172      console.log('onEndTracingComplete p1 with ' +
173                  this.traceEvents_.length + ' events.');
174      var e = new cr.Event('traceEnded');
175      e.events = this.traceEvents_;
176      this.dispatchEvent(e);
177    },
178
179    /**
180     * Tells browser to put up a load dialog and load the trace file
181     */
182    beginLoadTraceFile: function() {
183      chrome.send('loadTraceFile');
184    },
185
186    /**
187     * Called by the browser when a trace file is loaded.
188     */
189    onLoadTraceFileComplete: function(data) {
190      if (data.traceEvents) {
191        this.traceEvents_ = data.traceEvents;
192      } else { // path for loading traces saved without metadata
193        if (!data.length)
194          console.log('Expected an array when loading the trace file');
195        else
196          this.traceEvents_ = data;
197      }
198      var e = new cr.Event('loadTraceFileComplete');
199      e.events = this.traceEvents_;
200      this.dispatchEvent(e);
201    },
202
203    /**
204     * Called by the browser when loading a trace file was canceled.
205     */
206    onLoadTraceFileCanceled: function() {
207      cr.dispatchSimpleEvent(this, 'loadTraceFileCanceled');
208    },
209
210    /**
211     * Tells browser to put up a save dialog and save the trace file
212     */
213    beginSaveTraceFile: function(traceEvents) {
214      var data = {
215        traceEvents: traceEvents,
216        clientInfo: browserBridge.clientInfo,
217        gpuInfo: browserBridge.gpuInfo
218      };
219      chrome.send('saveTraceFile', [JSON.stringify(data)]);
220    },
221
222    /**
223     * Called by the browser when a trace file is saveed.
224     */
225    onSaveTraceFileComplete: function() {
226      cr.dispatchSimpleEvent(this, 'saveTraceFileComplete');
227    },
228
229    /**
230     * Called by the browser when saving a trace file was canceled.
231     */
232    onSaveTraceFileCanceled: function() {
233      cr.dispatchSimpleEvent(this, 'saveTraceFileCanceled');
234    },
235
236    selfTest: function() {
237      this.beginTracing();
238      window.setTimeout(this.endTracing.bind(This), 500);
239    }
240  };
241  return {
242    TracingController: TracingController
243  };
244});
245
246