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.
6 * Different data types that each require their own labelled axis.
8 var TimelineDataType
= {
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.
19 * Abstract class, doesn't implement onReceivedLogEntry.
21 var TimelineDataSeries
= (function() {
27 function TimelineDataSeries(dataType
) {
28 // List of DataPoints in chronological order.
29 this.dataPoints_
= [];
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.
36 // Whether or not the data series should be drawn.
37 this.isVisible_
= false;
39 this.cacheStartTime_
= null;
40 this.cacheStepSize_
= 0;
41 this.cacheValues_
= [];
44 TimelineDataSeries
.prototype = {
46 * Adds a DataPoint to |this| with the specified time and value.
47 * DataPoints are assumed to be received in chronological order.
49 addPoint: function(timeTicks
, value
) {
50 var time
= timeutil
.convertTimeTicksToDate(timeTicks
).getTime();
51 this.dataPoints_
.push(new DataPoint(time
, value
));
54 isVisible: function() {
55 return this.isVisible_
;
58 show: function(isVisible
) {
59 this.isVisible_
= isVisible
;
62 getColor: function() {
66 setColor: function(color
) {
70 getDataType: function() {
71 return this.dataType_
;
75 * Returns a list containing the values of the data series at |count|
76 * points, starting at |startTime|, and |stepSize| milliseconds apart.
77 * Caches values, so showing/hiding individual data series is fast, and
78 * derived data series can be efficiently computed, if we add any.
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_
;
89 this.cacheValues_
= this.getValuesInternal_(startTime
, stepSize
, count
);
90 this.cacheStartTime_
= startTime
;
91 this.cacheStepSize_
= stepSize
;
93 return this.cacheValues_
;
97 * Does all the work of getValues when we can't use cached data.
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 over each interval
103 * to create graphs more stable with respect to zooming.
105 getValuesInternal_: function(startTime
, stepSize
, count
) {
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
;
116 values
[i
] = currentValue
;
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.
128 function DataPoint(time
, value
) {
133 return TimelineDataSeries
;
137 * Tracks how many sources of the given type have seen a begin
138 * event of type |eventType| more recently than an end event.
140 var SourceCountDataSeries
= (function() {
143 var superClass
= TimelineDataSeries
;
148 function SourceCountDataSeries(sourceType
, eventType
) {
149 superClass
.call(this, TimelineDataType
.SOURCE_COUNT
);
150 this.sourceType_
= sourceType
;
151 this.eventType_
= eventType
;
153 // Map of sources for which we've seen a begin event more recently than an
154 // end event. Each such source has a value of "true". All others are
156 this.activeSources_
= {};
157 // Number of entries in |activeSources_|.
158 this.activeCount_
= 0;
161 SourceCountDataSeries
.prototype = {
162 // Inherit the superclass's methods.
163 __proto__
: superClass
.prototype,
165 onReceivedLogEntry: function(entry
) {
166 if (entry
.source
.type
!= this.sourceType_
||
167 entry
.type
!= this.eventType_
) {
171 if (entry
.phase
== EventPhase
.PHASE_BEGIN
) {
172 this.onBeginEvent(entry
.source
.id
, entry
.time
);
175 if (entry
.phase
== EventPhase
.PHASE_END
)
176 this.onEndEvent(entry
.source
.id
, entry
.time
);
180 * Called when the source with the specified id begins doing whatever we
181 * care about. If it's not already an active source, we add it to the map
182 * and add a data point.
184 onBeginEvent: function(id
, time
) {
185 if (this.activeSources_
[id
])
187 this.activeSources_
[id
] = true;
189 this.addPoint(time
, this.activeCount_
);
193 * Called when the source with the specified id stops doing whatever we
194 * care about. If it's an active source, we remove it from the map and add
197 onEndEvent: function(id
, time
) {
198 if (!this.activeSources_
[id
])
200 delete this.activeSources_
[id
];
202 this.addPoint(time
, this.activeCount_
);
206 return SourceCountDataSeries
;
210 * Tracks the number of sockets currently in use. Needs special handling of
211 * SSL sockets, so can't just use a normal SourceCountDataSeries.
213 var SocketsInUseDataSeries
= (function() {
216 var superClass
= SourceCountDataSeries
;
221 function SocketsInUseDataSeries() {
222 superClass
.call(this, EventSourceType
.SOCKET
, EventType
.SOCKET_IN_USE
);
225 SocketsInUseDataSeries
.prototype = {
226 // Inherit the superclass's methods.
227 __proto__
: superClass
.prototype,
229 onReceivedLogEntry: function(entry
) {
230 // SSL sockets have two nested SOCKET_IN_USE events. This is needed to
231 // mark SSL sockets as unused after SSL negotiation.
232 if (entry
.type
== EventType
.SSL_CONNECT
&&
233 entry
.phase
== EventPhase
.PHASE_END
) {
234 this.onEndEvent(entry
.source
.id
, entry
.time
);
237 superClass
.prototype.onReceivedLogEntry
.call(this, entry
);
241 return SocketsInUseDataSeries
;
245 * Tracks approximate data rate using individual data transfer events.
246 * Abstract class, doesn't implement onReceivedLogEntry.
248 var TransferRateDataSeries
= (function() {
251 var superClass
= TimelineDataSeries
;
256 function TransferRateDataSeries() {
257 superClass
.call(this, TimelineDataType
.BYTES_PER_SECOND
);
260 TransferRateDataSeries
.prototype = {
261 // Inherit the superclass's methods.
262 __proto__
: superClass
.prototype,
265 * Returns the average data rate over each interval, only taking into
266 * account transfers that occurred within each interval.
267 * TODO(mmenke): Do something better.
269 getValuesInternal_: function(startTime
, stepSize
, count
) {
270 // Find the first DataPoint after |startTime| - |stepSize|.
272 while (nextPoint
< this.dataPoints_
.length
&&
273 this.dataPoints_
[nextPoint
].time
< startTime
- stepSize
) {
278 var time
= startTime
;
279 for (var i
= 0; i
< count
; ++i
) {
280 // Calculate total bytes transferred from |time| - |stepSize|
281 // to |time|. We look at the transfers before |time| to give
282 // us generally non-varying values for a given time.
284 while (nextPoint
< this.dataPoints_
.length
&&
285 this.dataPoints_
[nextPoint
].time
< time
) {
286 transferred
+= this.dataPoints_
[nextPoint
].value
;
289 // Calculate bytes per second.
290 values
[i
] = 1000 * transferred
/ stepSize
;
297 return TransferRateDataSeries
;
301 * Tracks TCP and UDP transfer rate.
303 var NetworkTransferRateDataSeries
= (function() {
306 var superClass
= TransferRateDataSeries
;
309 * |tcpEvent| and |udpEvent| are the event types for data transfers using
310 * TCP and UDP, respectively.
313 function NetworkTransferRateDataSeries(tcpEvent
, udpEvent
) {
314 superClass
.call(this);
315 this.tcpEvent_
= tcpEvent
;
316 this.udpEvent_
= udpEvent
;
319 NetworkTransferRateDataSeries
.prototype = {
320 // Inherit the superclass's methods.
321 __proto__
: superClass
.prototype,
323 onReceivedLogEntry: function(entry
) {
324 if (entry
.type
!= this.tcpEvent_
&& entry
.type
!= this.udpEvent_
)
326 this.addPoint(entry
.time
, entry
.params
.byte_count
);
330 return NetworkTransferRateDataSeries
;
334 * Tracks disk cache read or write rate. Doesn't include clearing, opening,
335 * or dooming entries, as they don't have clear size values.
337 var DiskCacheTransferRateDataSeries
= (function() {
340 var superClass
= TransferRateDataSeries
;
345 function DiskCacheTransferRateDataSeries(eventType
) {
346 superClass
.call(this);
347 this.eventType_
= eventType
;
350 DiskCacheTransferRateDataSeries
.prototype = {
351 // Inherit the superclass's methods.
352 __proto__
: superClass
.prototype,
354 onReceivedLogEntry: function(entry
) {
355 if (entry
.source
.type
!= EventSourceType
.DISK_CACHE_ENTRY
||
356 entry
.type
!= this.eventType_
||
357 entry
.phase
!= EventPhase
.PHASE_END
) {
360 // The disk cache has a lot of 0-length writes, when truncating entries.
362 if (entry
.params
.bytes_copied
!= 0)
363 this.addPoint(entry
.time
, entry
.params
.bytes_copied
);
367 return DiskCacheTransferRateDataSeries
;