|
OLD | NEW |
---|---|
(Empty) | |
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 * Different data types that each require their own labelled axis. | |
7 */ | |
8 var TimelineDataType = { | |
9 SOURCE_COUNT: 0, | |
10 BYTES_PER_SECOND: 1 | |
11 }; | |
12 | |
13 /** | |
14 * A TimelineDataSeries collects an ordered series of (time, value) pairs, | |
15 * and converts them to graph points. It also keeps track of its color and | |
16 * current visibility state. DataSeries are solely responsible for tracking | |
17 * data, and do not send notifications on state changes. | |
18 * | |
19 * Abstract class, doesn't implement onReceivedLogEntry. | |
20 */ | |
21 var TimelineDataSeries = (function() { | |
22 'use strict'; | |
23 | |
24 /** | |
25 * @constructor | |
26 */ | |
27 function TimelineDataSeries(dataType) { | |
28 // List of DataPoints in chronological order. | |
29 this.dataPoints_ = []; | |
30 | |
31 // Data type of the DataSeries. This is used to scale all values with | |
32 // the same units in the same way. | |
33 this.dataType_ = dataType; | |
34 // Default color. Should always be overridden prior to display. | |
35 this.color_ = 'red'; | |
36 // Whether or not the data series should be drawn. | |
37 this.isVisible_ = false; | |
38 | |
39 this.cacheStartTime_ = null; | |
40 this.cacheStepSize_ = 0; | |
41 this.cacheValues_ = []; | |
42 } | |
43 | |
44 TimelineDataSeries.prototype = { | |
45 /** | |
46 * Adds a DataPoint to |this| with the specified time and value. | |
47 * DataPoints are assumed to be received in chronological order. | |
48 */ | |
49 addPoint: function(timeTicks, value) { | |
50 var time = timeutil.convertTimeTicksToDate(timeTicks).getTime(); | |
51 this.dataPoints_.push(new DataPoint(time, value)); | |
52 }, | |
53 | |
54 isVisible: function() { | |
55 return this.isVisible_; | |
56 }, | |
57 | |
58 show: function(isVisible) { | |
59 this.isVisible_ = isVisible; | |
60 }, | |
61 | |
62 color: function() { | |
eroman
2011/11/16 04:03:46
nit: for consistency, I suggest calling this getCo
mmenke
2011/11/16 18:39:35
Done.
| |
63 return this.color_; | |
64 }, | |
65 | |
66 setColor: function(color) { | |
67 this.color_ = color; | |
68 }, | |
69 | |
70 dataType: function() { | |
eroman
2011/11/16 04:03:46
nit: for consistency I suggest calling this getDat
mmenke
2011/11/16 18:39:35
Done.
| |
71 return this.dataType_; | |
72 }, | |
73 | |
74 /** | |
75 * Takes in a list of 2 or more times, in increasing order and equally | |
76 * spaced, and returns the value of the data series at each point. | |
77 * Caches values, so showing/hiding individual data series is fast, and | |
78 * derived data series can be efficiently computed, if we add any. | |
79 */ | |
80 getValues: function(startTime, stepSize, count) { | |
81 // Use cached values, if we can. | |
82 if (this.cacheStartTime_ == startTime && | |
83 this.cacheStepSize_ == stepSize && | |
84 this.cacheValues_.length == count) { | |
85 return this.cacheValues_; | |
86 } | |
87 | |
88 // Do all the work. | |
89 this.cacheValues_ = this.getValuesInternal_(startTime, stepSize, count); | |
90 | |
91 this.cacheStartTime_ = startTime; | |
92 this.cacheStepSize_ = stepSize; | |
93 return this.cacheValues_; | |
94 }, | |
95 | |
96 /** | |
97 * Does all the work of getValues when we can't use cached data. | |
98 * | |
99 * The default implementation just uses the |value| of the most recently | |
100 * seen DataPoint before each time, but other DataSeries may use some | |
101 * form of interpolation. | |
102 * TODO(mmenke): Consider returning the maximum value in a range, to get | |
103 * graphs more stable with respect to zooming. | |
104 */ | |
105 getValuesInternal_: function(startTime, stepSize, count) { | |
106 var values = []; | |
107 var nextPoint = 0; | |
108 var currentValue = 0; | |
109 var time = startTime; | |
110 for (var i = 0; i < count; ++i) { | |
111 while (nextPoint < this.dataPoints_.length && | |
112 this.dataPoints_[nextPoint].time < time) { | |
113 currentValue = this.dataPoints_[nextPoint].value; | |
114 ++nextPoint; | |
115 } | |
116 values[i] = currentValue; | |
117 time += stepSize; | |
118 } | |
119 return values; | |
120 } | |
121 }; | |
122 | |
123 /** | |
124 * A single point in a data series. Each point has a time, in the form of | |
125 * milliseconds since the Unix epoch, and a numeric value. | |
126 * @constructor | |
127 */ | |
128 function DataPoint(time, value) { | |
129 this.time = time; | |
130 this.value = value; | |
131 } | |
132 | |
133 return TimelineDataSeries; | |
134 })(); | |
135 | |
136 /** | |
137 * Tracks how many sources of the given type have seen a begin | |
138 * event of type |eventType| more recently than a corresponding | |
139 * end event. | |
140 */ | |
141 var SourceCountDataSeries = (function() { | |
142 'use strict'; | |
143 | |
144 var superClass = TimelineDataSeries; | |
145 | |
146 /** | |
147 * @constructor | |
148 */ | |
149 function SourceCountDataSeries(sourceType, eventType) { | |
150 superClass.call(this, TimelineDataType.SOURCE_COUNT); | |
151 this.sourceType_ = sourceType; | |
152 this.eventType_ = eventType; | |
153 | |
154 // Map of sources which we've seen a begin event more recently than an end | |
155 // event. Each such source has a value of "true". All others are | |
156 // undefined. | |
157 this.activeSources_ = {}; | |
158 // Number of sources in the previous list. | |
159 this.activeCount_ = 0; | |
160 } | |
161 | |
162 SourceCountDataSeries.prototype = { | |
163 // Inherit the superclass's methods. | |
164 __proto__: superClass.prototype, | |
165 | |
166 onReceivedLogEntry: function(entry) { | |
167 if (entry.source.type != this.sourceType_ || | |
168 entry.type != this.eventType_) { | |
169 return; | |
170 } | |
171 | |
172 if (entry.phase == LogEventPhase.PHASE_BEGIN) { | |
173 this.onBeginEvent(entry.source.id, entry.time); | |
174 return; | |
175 } | |
176 if (entry.phase == LogEventPhase.PHASE_END) | |
177 this.onEndEvent(entry.source.id, entry.time); | |
178 }, | |
179 | |
180 /** | |
181 * Called when the source with the specified id begins doing whatever we | |
182 * care about. If it's not already an active source, we add it to the map | |
183 * and add a data point. | |
184 */ | |
185 onBeginEvent: function(id, time) { | |
186 if (this.activeSources_[id]) | |
187 return; | |
188 this.activeSources_[id] = true; | |
189 ++this.activeCount_; | |
190 this.addPoint(time, this.activeCount_); | |
191 }, | |
192 | |
193 /** | |
194 * Called when the source with the specified id stops doing whatever we | |
195 * care about. If it's an active source, we remove it from the map and add | |
196 * a data point. | |
197 */ | |
198 onEndEvent: function(id, time) { | |
199 if (!this.activeSources_[id]) | |
200 return; | |
201 delete this.activeSources_[id]; | |
202 --this.activeCount_; | |
203 this.addPoint(time, this.activeCount_); | |
204 } | |
205 }; | |
206 | |
207 return SourceCountDataSeries; | |
208 })(); | |
209 | |
210 /** | |
211 * Tracks the number of sockets currently in use. Needs special handling of | |
212 * SSL sockets, so can't just use a normal SourceCountDataSeries. | |
213 */ | |
214 var SocketsInUseDataSeries = (function() { | |
215 'use strict'; | |
216 | |
217 var superClass = SourceCountDataSeries; | |
218 | |
219 /** | |
220 * @constructor | |
221 */ | |
222 function SocketsInUseDataSeries() { | |
223 superClass.call(this, LogSourceType.SOCKET, LogEventType.SOCKET_IN_USE); | |
224 } | |
225 | |
226 SocketsInUseDataSeries.prototype = { | |
227 // Inherit the superclass's methods. | |
228 __proto__: superClass.prototype, | |
229 | |
230 onReceivedLogEntry: function(entry) { | |
231 // SSL sockets have two nested SOCKET_IN_USE events. This is needed to | |
232 // mark SSL sockets as unused after SSL negotiation. | |
233 if (entry.type == LogEventType.SSL_CONNECT && | |
234 entry.phase == LogEventPhase.PHASE_END) { | |
235 this.onEndEvent(entry.source.id, entry.time); | |
236 return; | |
237 } | |
238 superClass.prototype.onReceivedLogEntry.call(this, entry); | |
239 } | |
240 }; | |
241 | |
242 return SocketsInUseDataSeries; | |
243 })(); | |
244 | |
245 /** | |
246 * Tracks approximate data rate using individual data transfer events. | |
247 * Abstract class, doesn't implement onReceivedLogEntry. | |
248 */ | |
249 var TransferRateDataSeries = (function() { | |
250 'use strict'; | |
251 | |
252 var superClass = TimelineDataSeries; | |
253 | |
254 /** | |
255 * @constructor | |
256 */ | |
257 function TransferRateDataSeries() { | |
258 superClass.call(this, TimelineDataType.BYTES_PER_SECOND); | |
259 } | |
260 | |
261 TransferRateDataSeries.prototype = { | |
262 // Inherit the superclass's methods. | |
263 __proto__: superClass.prototype, | |
264 | |
265 /** | |
266 * Returns the average data rate over each interval, only taking into | |
267 * account transfers that occurred within each interval. | |
268 * TODO(mmenke): Do something better. | |
eroman
2011/11/16 04:03:46
I have been having a hard time figuring exactly ho
mmenke
2011/11/16 18:39:35
Averaging over a time range is probably the right
| |
269 */ | |
270 getValuesInternal_: function(startTime, stepSize, count) { | |
271 // Find the first DataPoint after |startTime| - |stepSize|. | |
272 var nextPoint = 0; | |
273 while (nextPoint < this.dataPoints_.length && | |
274 this.dataPoints_[nextPoint].time < startTime - stepSize) { | |
275 ++nextPoint; | |
276 } | |
277 | |
278 var values = []; | |
279 var time = startTime; | |
280 for (var i = 0; i < count; ++i) { | |
281 // Calculate total bytes transferred from |time| - |stepSize| | |
282 // to |time|. We look at the transfers before |time| to give | |
283 // us generally non-varying values for a given time. | |
284 var transferred = 0; | |
285 while (nextPoint < this.dataPoints_.length && | |
286 this.dataPoints_[nextPoint].time < time) { | |
287 transferred += this.dataPoints_[nextPoint].value; | |
288 ++nextPoint; | |
289 } | |
290 // Calculate bytes per second. | |
291 values[i] = 1000 * transferred / stepSize; | |
292 time += stepSize; | |
293 } | |
294 return values; | |
295 } | |
296 }; | |
297 | |
298 return TransferRateDataSeries; | |
299 })(); | |
300 | |
301 /** | |
302 * Tracks TCP and UDP transfer rate. | |
303 */ | |
304 var NetworkTransferRateDataSeries = (function() { | |
305 'use strict'; | |
306 | |
307 var superClass = TransferRateDataSeries; | |
308 | |
309 /** | |
310 * |tcpEvent| and |udpEvent| are the event types for data transfers using | |
311 * TCP and UDP, respectively. | |
312 * @constructor | |
313 */ | |
314 function NetworkTransferRateDataSeries(tcpEvent, udpEvent) { | |
315 superClass.call(this); | |
316 this.tcpEvent_ = tcpEvent; | |
317 this.udpEvent_ = udpEvent; | |
318 } | |
319 | |
320 NetworkTransferRateDataSeries.prototype = { | |
321 // Inherit the superclass's methods. | |
322 __proto__: superClass.prototype, | |
323 | |
324 onReceivedLogEntry: function(entry) { | |
325 if (entry.type != this.tcpEvent_ && entry.type != this.udpEvent_) | |
326 return; | |
327 this.addPoint(entry.time, entry.params.byte_count); | |
328 }, | |
329 }; | |
330 | |
331 return NetworkTransferRateDataSeries; | |
332 })(); | |
333 | |
334 /** | |
335 * Tracks combined disk cache read or write rate. Doesn't include clearing, | |
336 * opening, or dooming entries, as they don't have clear size values. | |
337 */ | |
338 var DiskCacheTransferRateDataSeries = (function() { | |
339 'use strict'; | |
340 | |
341 var superClass = TransferRateDataSeries; | |
342 | |
343 /** | |
344 * @constructor | |
345 */ | |
346 function DiskCacheTransferRateDataSeries(eventType) { | |
347 superClass.call(this); | |
348 this.eventType_ = eventType; | |
349 } | |
350 | |
351 DiskCacheTransferRateDataSeries.prototype = { | |
352 // Inherit the superclass's methods. | |
353 __proto__: superClass.prototype, | |
354 | |
355 onReceivedLogEntry: function(entry) { | |
356 if (entry.source.type != LogSourceType.DISK_CACHE_ENTRY || | |
357 entry.type != this.eventType_ || | |
358 entry.phase != LogEventPhase.PHASE_END) { | |
359 return; | |
360 } | |
361 // The disk cache has a lot of 0-length writes, when truncating entries. | |
362 // Ignore those. | |
363 if (entry.params.bytes_copied != 0) | |
364 this.addPoint(entry.time, entry.params.bytes_copied); | |
365 } | |
366 }; | |
367 | |
368 return DiskCacheTransferRateDataSeries; | |
369 })(); | |
OLD | NEW |