15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2011 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#ifndef NET_BASE_BANDWIDTH_METRICS_H_ 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define NET_BASE_BANDWIDTH_METRICS_H_ 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <list> 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h" 11eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/metrics/histogram.h" 12eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h" 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace net { 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Tracks statistics about the bandwidth metrics over time. In order to 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// measure, this class needs to know when individual streams are in progress, 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// so that it can know when to discount idle time. The BandwidthMetrics 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// is unidirectional - it should only be used to record upload or download 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// bandwidth, but not both. 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Note, the easiest thing to do is to just measure each stream and average 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// them or add them. However, this does not work. If multiple streams are in 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// progress concurrently, you have to look at the aggregate bandwidth at any 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// point in time. 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Example: 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Imagine 4 streams opening and closing with overlapping time. 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// We can't measure bandwidth by looking at any individual stream. 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// We can only measure actual bandwidth by looking at the bandwidth 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// across all open streams. 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Time ---------------------------------------> 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// s1 +----------------+ 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// s2 +----------------+ 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// s3 +--------------+ 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// s4 +--------------+ 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Example usage: 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// BandwidthMetrics tracker; 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// // When a stream is created 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// tracker.StartStream(); 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// // When data is transferred on any stream 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// tracker.RecordSample(bytes); 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// // When the stream is finished 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// tracker.StopStream(); 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// NOTE: This class is not thread safe. 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class BandwidthMetrics { 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public: 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BandwidthMetrics() 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : num_streams_in_progress_(0), 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) num_data_samples_(0), 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) data_sum_(0.0), 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bytes_since_last_start_(0) { 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Get the bandwidth. Returns Kbps (kilo-bits-per-second). 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) double bandwidth() const { 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return data_sum_ / num_data_samples_; 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Record that we've started a stream. 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void StartStream() { 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If we're the only stream, we've finished some idle time. Record a new 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // timestamp to indicate the start of data flow. 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (++num_streams_in_progress_ == 1) { 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) last_start_ = base::TimeTicks::HighResNow(); 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bytes_since_last_start_ = 0; 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Track that we've completed a stream. 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void StopStream() { 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (--num_streams_in_progress_ == 0) { 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We don't use small streams when tracking bandwidth because they are not 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // precise; imagine a 25 byte stream. The sample is too small to make 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // a good measurement. 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 20KB is an arbitrary value. We might want to use a lesser value. 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static const int64 kRecordSizeThreshold = 20 * 1024; 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (bytes_since_last_start_ < kRecordSizeThreshold) 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::TimeDelta delta = base::TimeTicks::HighResNow() - last_start_; 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) double ms = delta.InMillisecondsF(); 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (ms > 0.0) { 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) double kbps = static_cast<double>(bytes_since_last_start_) * 8 / ms; 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ++num_data_samples_; 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) data_sum_ += kbps; 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) VLOG(1) << "Bandwidth: " << kbps 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) << "Kbps (avg " << bandwidth() << "Kbps)"; 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int kbps_int = static_cast<int>(kbps); 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UMA_HISTOGRAM_COUNTS_10000("Net.DownloadBandwidth", kbps_int); 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Add a sample of the number of bytes read from the network into the tracker. 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void RecordBytes(int bytes) { 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(num_streams_in_progress_); 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bytes_since_last_start_ += static_cast<int64>(bytes); 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private: 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int num_streams_in_progress_; // The number of streams in progress. 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(mbelshe): Use a rolling buffer of 30 samples instead of an average. 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int num_data_samples_; // The number of samples collected. 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) double data_sum_; // The sum of all samples collected. 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int64 bytes_since_last_start_; // Bytes tracked during this "session". 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::TimeTicks last_start_; // Timestamp of the begin of this "session". 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// A utility class for managing the lifecycle of a measured stream. 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// It is important that we not leave unclosed streams, and this class helps 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// ensure we always stop them. 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ScopedBandwidthMetrics { 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public: 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ScopedBandwidthMetrics(); 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ~ScopedBandwidthMetrics(); 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void StartStream(); 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void StopStream(); 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void RecordBytes(int bytes); 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private: 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool started_; 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace net 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif // NET_BASE_BANDWIDTH_METRICS_H_ 137