Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(786)

Side by Side Diff: chrome/browser/resources/net_internals/timeline_data_series.js

Issue 8474001: Add a timeline view to about:net-internals. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Update comments Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(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 })();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698