|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | /** | 
|  | * Different data types that each require their own labelled axis. | 
|  | */ | 
|  | var TimelineDataType = { | 
|  | SOURCE_COUNT: 0, | 
|  | BYTES_PER_SECOND: 1 | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * A TimelineDataSeries collects an ordered series of (time, value) pairs, | 
|  | * and converts them to graph points.  It also keeps track of its color and | 
|  | * current visibility state.  DataSeries are solely responsible for tracking | 
|  | * data, and do not send notifications on state changes. | 
|  | * | 
|  | * Abstract class, doesn't implement onReceivedLogEntry. | 
|  | */ | 
|  | var TimelineDataSeries = (function() { | 
|  | 'use strict'; | 
|  |  | 
|  | /** | 
|  | * @constructor | 
|  | */ | 
|  | function TimelineDataSeries(dataType) { | 
|  | // List of DataPoints in chronological order. | 
|  | this.dataPoints_ = []; | 
|  |  | 
|  | // Data type of the DataSeries.  This is used to scale all values with | 
|  | // the same units in the same way. | 
|  | this.dataType_ = dataType; | 
|  | // Default color.  Should always be overridden prior to display. | 
|  | this.color_ = 'red'; | 
|  | // Whether or not the data series should be drawn. | 
|  | this.isVisible_ = false; | 
|  |  | 
|  | this.cacheStartTime_ = null; | 
|  | this.cacheStepSize_ = 0; | 
|  | this.cacheValues_ = []; | 
|  | } | 
|  |  | 
|  | TimelineDataSeries.prototype = { | 
|  | /** | 
|  | * Adds a DataPoint to |this| with the specified time and value. | 
|  | * DataPoints are assumed to be received in chronological order. | 
|  | */ | 
|  | addPoint: function(timeTicks, value) { | 
|  | var time = timeutil.convertTimeTicksToDate(timeTicks).getTime(); | 
|  | this.dataPoints_.push(new DataPoint(time, value)); | 
|  | }, | 
|  |  | 
|  | isVisible: function() { | 
|  | return this.isVisible_; | 
|  | }, | 
|  |  | 
|  | show: function(isVisible) { | 
|  | this.isVisible_ = isVisible; | 
|  | }, | 
|  |  | 
|  | getColor: function() { | 
|  | return this.color_; | 
|  | }, | 
|  |  | 
|  | setColor: function(color) { | 
|  | this.color_ = color; | 
|  | }, | 
|  |  | 
|  | getDataType: function() { | 
|  | return this.dataType_; | 
|  | }, | 
|  |  | 
|  | /** | 
|  | * Returns a list containing the values of the data series at |count| | 
|  | * points, starting at |startTime|, and |stepSize| milliseconds apart. | 
|  | * Caches values, so showing/hiding individual data series is fast, and | 
|  | * derived data series can be efficiently computed, if we add any. | 
|  | */ | 
|  | getValues: function(startTime, stepSize, count) { | 
|  | // Use cached values, if we can. | 
|  | if (this.cacheStartTime_ == startTime && | 
|  | this.cacheStepSize_ == stepSize && | 
|  | this.cacheValues_.length == count) { | 
|  | return this.cacheValues_; | 
|  | } | 
|  |  | 
|  | // Do all the work. | 
|  | this.cacheValues_ = this.getValuesInternal_(startTime, stepSize, count); | 
|  | this.cacheStartTime_ = startTime; | 
|  | this.cacheStepSize_ = stepSize; | 
|  |  | 
|  | return this.cacheValues_; | 
|  | }, | 
|  |  | 
|  | /** | 
|  | * Does all the work of getValues when we can't use cached data. | 
|  | * | 
|  | * The default implementation just uses the |value| of the most recently | 
|  | * seen DataPoint before each time, but other DataSeries may use some | 
|  | * form of interpolation. | 
|  | * TODO(mmenke):  Consider returning the maximum value over each interval | 
|  | *                to create graphs more stable with respect to zooming. | 
|  | */ | 
|  | getValuesInternal_: function(startTime, stepSize, count) { | 
|  | var values = []; | 
|  | var nextPoint = 0; | 
|  | var currentValue = 0; | 
|  | var time = startTime; | 
|  | for (var i = 0; i < count; ++i) { | 
|  | while (nextPoint < this.dataPoints_.length && | 
|  | this.dataPoints_[nextPoint].time < time) { | 
|  | currentValue = this.dataPoints_[nextPoint].value; | 
|  | ++nextPoint; | 
|  | } | 
|  | values[i] = currentValue; | 
|  | time += stepSize; | 
|  | } | 
|  | return values; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * A single point in a data series.  Each point has a time, in the form of | 
|  | * milliseconds since the Unix epoch, and a numeric value. | 
|  | * @constructor | 
|  | */ | 
|  | function DataPoint(time, value) { | 
|  | this.time = time; | 
|  | this.value = value; | 
|  | } | 
|  |  | 
|  | return TimelineDataSeries; | 
|  | })(); | 
|  |  | 
|  | /** | 
|  | * Tracks how many sources of the given type have seen a begin | 
|  | * event of type |eventType| more recently than an end event. | 
|  | */ | 
|  | var SourceCountDataSeries = (function() { | 
|  | 'use strict'; | 
|  |  | 
|  | var superClass = TimelineDataSeries; | 
|  |  | 
|  | /** | 
|  | * @constructor | 
|  | */ | 
|  | function SourceCountDataSeries(sourceType, eventType) { | 
|  | superClass.call(this, TimelineDataType.SOURCE_COUNT); | 
|  | this.sourceType_ = sourceType; | 
|  | this.eventType_ = eventType; | 
|  |  | 
|  | // Map of sources for which we've seen a begin event more recently than an | 
|  | // end event.  Each such source has a value of "true".  All others are | 
|  | // undefined. | 
|  | this.activeSources_ = {}; | 
|  | // Number of entries in |activeSources_|. | 
|  | this.activeCount_ = 0; | 
|  | } | 
|  |  | 
|  | SourceCountDataSeries.prototype = { | 
|  | // Inherit the superclass's methods. | 
|  | __proto__: superClass.prototype, | 
|  |  | 
|  | onReceivedLogEntry: function(entry) { | 
|  | if (entry.source.type != this.sourceType_ || | 
|  | entry.type != this.eventType_) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (entry.phase == EventPhase.PHASE_BEGIN) { | 
|  | this.onBeginEvent(entry.source.id, entry.time); | 
|  | return; | 
|  | } | 
|  | if (entry.phase == EventPhase.PHASE_END) | 
|  | this.onEndEvent(entry.source.id, entry.time); | 
|  | }, | 
|  |  | 
|  | /** | 
|  | * Called when the source with the specified id begins doing whatever we | 
|  | * care about.  If it's not already an active source, we add it to the map | 
|  | * and add a data point. | 
|  | */ | 
|  | onBeginEvent: function(id, time) { | 
|  | if (this.activeSources_[id]) | 
|  | return; | 
|  | this.activeSources_[id] = true; | 
|  | ++this.activeCount_; | 
|  | this.addPoint(time, this.activeCount_); | 
|  | }, | 
|  |  | 
|  | /** | 
|  | * Called when the source with the specified id stops doing whatever we | 
|  | * care about.  If it's an active source, we remove it from the map and add | 
|  | * a data point. | 
|  | */ | 
|  | onEndEvent: function(id, time) { | 
|  | if (!this.activeSources_[id]) | 
|  | return; | 
|  | delete this.activeSources_[id]; | 
|  | --this.activeCount_; | 
|  | this.addPoint(time, this.activeCount_); | 
|  | } | 
|  | }; | 
|  |  | 
|  | return SourceCountDataSeries; | 
|  | })(); | 
|  |  | 
|  | /** | 
|  | * Tracks the number of sockets currently in use.  Needs special handling of | 
|  | * SSL sockets, so can't just use a normal SourceCountDataSeries. | 
|  | */ | 
|  | var SocketsInUseDataSeries = (function() { | 
|  | 'use strict'; | 
|  |  | 
|  | var superClass = SourceCountDataSeries; | 
|  |  | 
|  | /** | 
|  | * @constructor | 
|  | */ | 
|  | function SocketsInUseDataSeries() { | 
|  | superClass.call(this, EventSourceType.SOCKET, EventType.SOCKET_IN_USE); | 
|  | } | 
|  |  | 
|  | SocketsInUseDataSeries.prototype = { | 
|  | // Inherit the superclass's methods. | 
|  | __proto__: superClass.prototype, | 
|  |  | 
|  | onReceivedLogEntry: function(entry) { | 
|  | // SSL sockets have two nested SOCKET_IN_USE events.  This is needed to | 
|  | // mark SSL sockets as unused after SSL negotiation. | 
|  | if (entry.type == EventType.SSL_CONNECT && | 
|  | entry.phase == EventPhase.PHASE_END) { | 
|  | this.onEndEvent(entry.source.id, entry.time); | 
|  | return; | 
|  | } | 
|  | superClass.prototype.onReceivedLogEntry.call(this, entry); | 
|  | } | 
|  | }; | 
|  |  | 
|  | return SocketsInUseDataSeries; | 
|  | })(); | 
|  |  | 
|  | /** | 
|  | * Tracks approximate data rate using individual data transfer events. | 
|  | * Abstract class, doesn't implement onReceivedLogEntry. | 
|  | */ | 
|  | var TransferRateDataSeries = (function() { | 
|  | 'use strict'; | 
|  |  | 
|  | var superClass = TimelineDataSeries; | 
|  |  | 
|  | /** | 
|  | * @constructor | 
|  | */ | 
|  | function TransferRateDataSeries() { | 
|  | superClass.call(this, TimelineDataType.BYTES_PER_SECOND); | 
|  | } | 
|  |  | 
|  | TransferRateDataSeries.prototype = { | 
|  | // Inherit the superclass's methods. | 
|  | __proto__: superClass.prototype, | 
|  |  | 
|  | /** | 
|  | * Returns the average data rate over each interval, only taking into | 
|  | * account transfers that occurred within each interval. | 
|  | * TODO(mmenke): Do something better. | 
|  | */ | 
|  | getValuesInternal_: function(startTime, stepSize, count) { | 
|  | // Find the first DataPoint after |startTime| - |stepSize|. | 
|  | var nextPoint = 0; | 
|  | while (nextPoint < this.dataPoints_.length && | 
|  | this.dataPoints_[nextPoint].time < startTime - stepSize) { | 
|  | ++nextPoint; | 
|  | } | 
|  |  | 
|  | var values = []; | 
|  | var time = startTime; | 
|  | for (var i = 0; i < count; ++i) { | 
|  | // Calculate total bytes transferred from |time| - |stepSize| | 
|  | // to |time|.  We look at the transfers before |time| to give | 
|  | // us generally non-varying values for a given time. | 
|  | var transferred = 0; | 
|  | while (nextPoint < this.dataPoints_.length && | 
|  | this.dataPoints_[nextPoint].time < time) { | 
|  | transferred += this.dataPoints_[nextPoint].value; | 
|  | ++nextPoint; | 
|  | } | 
|  | // Calculate bytes per second. | 
|  | values[i] = 1000 * transferred / stepSize; | 
|  | time += stepSize; | 
|  | } | 
|  | return values; | 
|  | } | 
|  | }; | 
|  |  | 
|  | return TransferRateDataSeries; | 
|  | })(); | 
|  |  | 
|  | /** | 
|  | * Tracks TCP and UDP transfer rate. | 
|  | */ | 
|  | var NetworkTransferRateDataSeries = (function() { | 
|  | 'use strict'; | 
|  |  | 
|  | var superClass = TransferRateDataSeries; | 
|  |  | 
|  | /** | 
|  | * |tcpEvent| and |udpEvent| are the event types for data transfers using | 
|  | * TCP and UDP, respectively. | 
|  | * @constructor | 
|  | */ | 
|  | function NetworkTransferRateDataSeries(tcpEvent, udpEvent) { | 
|  | superClass.call(this); | 
|  | this.tcpEvent_ = tcpEvent; | 
|  | this.udpEvent_ = udpEvent; | 
|  | } | 
|  |  | 
|  | NetworkTransferRateDataSeries.prototype = { | 
|  | // Inherit the superclass's methods. | 
|  | __proto__: superClass.prototype, | 
|  |  | 
|  | onReceivedLogEntry: function(entry) { | 
|  | if (entry.type != this.tcpEvent_ && entry.type != this.udpEvent_) | 
|  | return; | 
|  | this.addPoint(entry.time, entry.params.byte_count); | 
|  | }, | 
|  | }; | 
|  |  | 
|  | return NetworkTransferRateDataSeries; | 
|  | })(); | 
|  |  | 
|  | /** | 
|  | * Tracks disk cache read or write rate.  Doesn't include clearing, opening, | 
|  | * or dooming entries, as they don't have clear size values. | 
|  | */ | 
|  | var DiskCacheTransferRateDataSeries = (function() { | 
|  | 'use strict'; | 
|  |  | 
|  | var superClass = TransferRateDataSeries; | 
|  |  | 
|  | /** | 
|  | * @constructor | 
|  | */ | 
|  | function DiskCacheTransferRateDataSeries(eventType) { | 
|  | superClass.call(this); | 
|  | this.eventType_ = eventType; | 
|  | } | 
|  |  | 
|  | DiskCacheTransferRateDataSeries.prototype = { | 
|  | // Inherit the superclass's methods. | 
|  | __proto__: superClass.prototype, | 
|  |  | 
|  | onReceivedLogEntry: function(entry) { | 
|  | if (entry.source.type != EventSourceType.DISK_CACHE_ENTRY || | 
|  | entry.type != this.eventType_ || | 
|  | entry.phase != EventPhase.PHASE_END) { | 
|  | return; | 
|  | } | 
|  | // The disk cache has a lot of 0-length writes, when truncating entries. | 
|  | // Ignore those. | 
|  | if (entry.params.bytes_copied != 0) | 
|  | this.addPoint(entry.time, entry.params.bytes_copied); | 
|  | } | 
|  | }; | 
|  |  | 
|  | return DiskCacheTransferRateDataSeries; | 
|  | })(); |