Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / resources / net_internals / timeline_data_series.js
blob9a21d3c3fa08c42c16a853550a77de4dabea6656
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.
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
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';
24   /**
25    * @constructor
26    */
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.
35     this.color_ = 'red';
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_ = [];
42   }
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     },
54     isVisible: function() {
55       return this.isVisible_;
56     },
58     show: function(isVisible) {
59       this.isVisible_ = isVisible;
60     },
62     getColor: function() {
63       return this.color_;
64     },
66     setColor: function(color) {
67       this.color_ = color;
68     },
70     getDataType: function() {
71       return this.dataType_;
72     },
74     /**
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.
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       }
88       // Do all the work.
89       this.cacheValues_ = this.getValuesInternal_(startTime, stepSize, count);
90       this.cacheStartTime_ = startTime;
91       this.cacheStepSize_ = stepSize;
93       return this.cacheValues_;
94     },
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 over each interval
103      *                to create 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   };
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   }
133   return TimelineDataSeries;
134 })();
137  * Tracks how many sources of the given type have seen a begin
138  * event of type |eventType| more recently than an end event.
139  */
140 var SourceCountDataSeries = (function() {
141   'use strict';
143   var superClass = TimelineDataSeries;
145   /**
146    * @constructor
147    */
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
155     // undefined.
156     this.activeSources_ = {};
157     // Number of entries in |activeSources_|.
158     this.activeCount_ = 0;
159   }
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_) {
168         return;
169       }
171       if (entry.phase == EventPhase.PHASE_BEGIN) {
172         this.onBeginEvent(entry.source.id, entry.time);
173         return;
174       }
175       if (entry.phase == EventPhase.PHASE_END)
176         this.onEndEvent(entry.source.id, entry.time);
177     },
179     /**
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.
183      */
184     onBeginEvent: function(id, time) {
185       if (this.activeSources_[id])
186         return;
187       this.activeSources_[id] = true;
188       ++this.activeCount_;
189       this.addPoint(time, this.activeCount_);
190     },
192     /**
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
195      * a data point.
196      */
197     onEndEvent: function(id, time) {
198       if (!this.activeSources_[id])
199         return;
200       delete this.activeSources_[id];
201       --this.activeCount_;
202       this.addPoint(time, this.activeCount_);
203     }
204   };
206   return SourceCountDataSeries;
207 })();
210  * Tracks the number of sockets currently in use.  Needs special handling of
211  * SSL sockets, so can't just use a normal SourceCountDataSeries.
212  */
213 var SocketsInUseDataSeries = (function() {
214   'use strict';
216   var superClass = SourceCountDataSeries;
218   /**
219    * @constructor
220    */
221   function SocketsInUseDataSeries() {
222     superClass.call(this, EventSourceType.SOCKET, EventType.SOCKET_IN_USE);
223   }
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);
235         return;
236       }
237       superClass.prototype.onReceivedLogEntry.call(this, entry);
238     }
239   };
241   return SocketsInUseDataSeries;
242 })();
245  * Tracks approximate data rate using individual data transfer events.
246  * Abstract class, doesn't implement onReceivedLogEntry.
247  */
248 var TransferRateDataSeries = (function() {
249   'use strict';
251   var superClass = TimelineDataSeries;
253   /**
254    * @constructor
255    */
256   function TransferRateDataSeries() {
257     superClass.call(this, TimelineDataType.BYTES_PER_SECOND);
258   }
260   TransferRateDataSeries.prototype = {
261     // Inherit the superclass's methods.
262     __proto__: superClass.prototype,
264     /**
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.
268      */
269     getValuesInternal_: function(startTime, stepSize, count) {
270       // Find the first DataPoint after |startTime| - |stepSize|.
271       var nextPoint = 0;
272       while (nextPoint < this.dataPoints_.length &&
273              this.dataPoints_[nextPoint].time < startTime - stepSize) {
274         ++nextPoint;
275       }
277       var values = [];
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.
283         var transferred = 0;
284         while (nextPoint < this.dataPoints_.length &&
285                this.dataPoints_[nextPoint].time < time) {
286           transferred += this.dataPoints_[nextPoint].value;
287           ++nextPoint;
288         }
289         // Calculate bytes per second.
290         values[i] = 1000 * transferred / stepSize;
291         time += stepSize;
292       }
293       return values;
294     }
295   };
297   return TransferRateDataSeries;
298 })();
301  * Tracks TCP and UDP transfer rate.
302  */
303 var NetworkTransferRateDataSeries = (function() {
304   'use strict';
306   var superClass = TransferRateDataSeries;
308   /**
309    * |tcpEvent| and |udpEvent| are the event types for data transfers using
310    * TCP and UDP, respectively.
311    * @constructor
312    */
313   function NetworkTransferRateDataSeries(tcpEvent, udpEvent) {
314     superClass.call(this);
315     this.tcpEvent_ = tcpEvent;
316     this.udpEvent_ = udpEvent;
317   }
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_)
325         return;
326       this.addPoint(entry.time, entry.params.byte_count);
327     },
328   };
330   return NetworkTransferRateDataSeries;
331 })();
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.
336  */
337 var DiskCacheTransferRateDataSeries = (function() {
338   'use strict';
340   var superClass = TransferRateDataSeries;
342   /**
343    * @constructor
344    */
345   function DiskCacheTransferRateDataSeries(eventType) {
346     superClass.call(this);
347     this.eventType_ = eventType;
348   }
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) {
358         return;
359       }
360       // The disk cache has a lot of 0-length writes, when truncating entries.
361       // Ignore those.
362       if (entry.params.bytes_copied != 0)
363         this.addPoint(entry.time, entry.params.bytes_copied);
364     }
365   };
367   return DiskCacheTransferRateDataSeries;
368 })();