Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / trace-viewer / trace_viewer / tracing / importer / timeline_stream_importer.js
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 Implements a WebSocket client that receives
9  * a stream of slices from a server.
10  *
11  */
12
13 tvcm.require('tvcm.events');
14 tvcm.require('tracing.trace_model');
15 tvcm.require('tracing.trace_model.slice');
16
17 tvcm.exportTo('tracing.importer', function() {
18
19   var STATE_PAUSED = 0x1;
20   var STATE_CAPTURING = 0x2;
21
22   /**
23    * Converts a stream of trace data from a websocket into a model.
24    *
25    * Events consumed by this importer have the following JSON structure:
26    *
27    * {
28    *   'cmd': 'commandName',
29    *   ... command specific data
30    * }
31    *
32    * The importer understands 2 commands:
33    *      'ptd' (Process Thread Data)
34    *      'pcd' (Process Counter Data)
35    *
36    * The command specific data is as follows:
37    *
38    * {
39    *   'pid': 'Remote Process Id',
40    *   'td': {
41    *                  'n': 'Thread Name Here',
42    *                  's: [ {
43    *                              'l': 'Slice Label',
44    *                              's': startTime,
45    *                              'e': endTime
46    *                              }, ... ]
47    *         }
48    * }
49    *
50    * {
51    *  'pid' 'Remote Process Id',
52    *  'cd': {
53    *      'n': 'Counter Name',
54    *      'sn': ['Series Name',...]
55    *      'sc': [seriesColor, ...]
56    *      'c': [
57    *            {
58    *              't': timestamp,
59    *              'v': [value0, value1, ...]
60    *            },
61    *            ....
62    *           ]
63    *       }
64    * }
65    * @param {Model} model that will be updated
66    * when events are received.
67    * @constructor
68    */
69   function TimelineStreamImporter(model) {
70     var self = this;
71     this.model_ = model;
72     this.connection_ = undefined;
73     this.state_ = STATE_CAPTURING;
74     this.connectionOpenHandler_ =
75         this.connectionOpenHandler_.bind(this);
76     this.connectionCloseHandler_ =
77         this.connectionCloseHandler_.bind(this);
78     this.connectionErrorHandler_ =
79         this.connectionErrorHandler_.bind(this);
80     this.connectionMessageHandler_ =
81         this.connectionMessageHandler_.bind(this);
82   }
83
84   TimelineStreamImporter.prototype = {
85     __proto__: tvcm.EventTarget.prototype,
86
87     cleanup_: function() {
88       if (!this.connection_)
89         return;
90       this.connection_.removeEventListener('open',
91           this.connectionOpenHandler_);
92       this.connection_.removeEventListener('close',
93           this.connectionCloseHandler_);
94       this.connection_.removeEventListener('error',
95           this.connectionErrorHandler_);
96       this.connection_.removeEventListener('message',
97           this.connectionMessageHandler_);
98     },
99
100     connectionOpenHandler_: function() {
101       this.dispatchEvent({'type': 'connect'});
102     },
103
104     connectionCloseHandler_: function() {
105       this.dispatchEvent({'type': 'disconnect'});
106       this.cleanup_();
107     },
108
109     connectionErrorHandler_: function() {
110       this.dispatchEvent({'type': 'connectionerror'});
111       this.cleanup_();
112     },
113
114     connectionMessageHandler_: function(event) {
115       var packet = JSON.parse(event.data);
116       var command = packet['cmd'];
117       var pid = packet['pid'];
118       var modelDirty = false;
119       if (command == 'ptd') {
120         var process = this.model_.getOrCreateProcess(pid);
121         var threadData = packet['td'];
122         var threadName = threadData['n'];
123         var threadSlices = threadData['s'];
124         var thread = process.getOrCreateThread(threadName);
125         for (var s = 0; s < threadSlices.length; s++) {
126           var slice = threadSlices[s];
127           thread.sliceGroup.pushSlice(new tracing.trace_model.ThreadSlice(
128               'streamed',
129               slice['l'],
130               0,
131               slice['s'],
132               {},
133               slice['e'] - slice['s']));
134         }
135         modelDirty = true;
136       } else if (command == 'pcd') {
137         var process = this.model_.getOrCreateProcess(pid);
138         var counterData = packet['cd'];
139         var counterName = counterData['n'];
140         var counterSeriesNames = counterData['sn'];
141         var counterSeriesColors = counterData['sc'];
142         var counterValues = counterData['c'];
143         var counter = process.getOrCreateCounter('streamed', counterName);
144         if (counterSeriesNames.length != counterSeriesColors.length) {
145           this.model_.importWarning({
146             type: 'parse_error',
147             message: 'Streamed counter name length does not match' +
148                 ' counter color length' + counterSeriesNames.length +
149                 ' vs ' + counterSeriesColors.length
150           });
151           return;
152         }
153         if (counter.series.length === 0) {
154           for (var i = 0; i < counterSeriesNames.length; ++i) {
155             counter.addSeries(new tracing.trace_model.CounterSeries(
156                 counterSeriesNames[i], counterSeriesColors[i]));
157           }
158         } else {
159           if (counter.series.length != counterSeriesNames.length) {
160             this.model_.importWarning({
161               type: 'parse_error',
162               message: 'Streamed counter ' + counterName +
163                   ' changed number of seriesNames'
164             });
165             return;
166           } else {
167             for (var i = 0; i < counter.series.length; i++) {
168               var oldSeriesName = counter.series[i].name;
169               var newSeriesName = counterSeriesNames[i];
170
171               if (oldSeriesName != newSeriesName) {
172                 this.model_.importWarning({
173                   type: 'parse_error',
174                   message: 'Streamed counter ' + counterName +
175                       ' series name changed from ' + oldSeriesName + ' to ' +
176                       newSeriesName
177                 });
178                 return;
179               }
180             }
181           }
182         }
183         for (var c = 0; c < counterValues.length; c++) {
184           var count = counterValues[c];
185           var ts = count['t'];
186           var values = count['v'];
187           for (var i = 0; i < values.length; ++i) {
188             counter.series[i].addCounterSample(ts, values[i]);
189           }
190         }
191         modelDirty = true;
192       }
193       if (modelDirty == true) {
194         this.model_.updateBounds();
195         this.dispatchEvent({'type': 'modelchange',
196           'model': this.model_});
197       }
198     },
199
200     get connected() {
201       if (this.connection_ !== undefined &&
202           this.connection_.readyState == WebSocket.OPEN) {
203         return true;
204       }
205       return false;
206     },
207
208     get paused() {
209       return this.state_ == STATE_PAUSED;
210     },
211
212     /**
213      * Connects the stream to a websocket.
214      * @param {WebSocket} wsConnection The websocket to use for the stream.
215      */
216     connect: function(wsConnection) {
217       this.connection_ = wsConnection;
218       this.connection_.addEventListener('open',
219           this.connectionOpenHandler_);
220       this.connection_.addEventListener('close',
221           this.connectionCloseHandler_);
222       this.connection_.addEventListener('error',
223           this.connectionErrorHandler_);
224       this.connection_.addEventListener('message',
225           this.connectionMessageHandler_);
226     },
227
228     pause: function() {
229       if (this.state_ == STATE_PAUSED)
230         throw new Error('Already paused.');
231       if (!this.connection_)
232         throw new Error('Not connected.');
233       this.connection_.send(JSON.stringify({'cmd': 'pause'}));
234       this.state_ = STATE_PAUSED;
235     },
236
237     resume: function() {
238       if (this.state_ == STATE_CAPTURING)
239         throw new Error('Already capturing.');
240       if (!this.connection_)
241         throw new Error('Not connected.');
242       this.connection_.send(JSON.stringify({'cmd': 'resume'}));
243       this.state_ = STATE_CAPTURING;
244     }
245   };
246
247   return {
248     TimelineStreamImporter: TimelineStreamImporter
249   };
250 });