|  | // Copyright (c) 2011 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. | 
|  |  | 
|  | #ifndef NET_BASE_BANDWIDTH_METRICS_H_ | 
|  | #define NET_BASE_BANDWIDTH_METRICS_H_ | 
|  |  | 
|  | #include <list> | 
|  |  | 
|  | #include "base/logging.h" | 
|  | #include "base/metrics/histogram.h" | 
|  | #include "base/time/time.h" | 
|  |  | 
|  | namespace net { | 
|  |  | 
|  | // Tracks statistics about the bandwidth metrics over time.  In order to | 
|  | // measure, this class needs to know when individual streams are in progress, | 
|  | // so that it can know when to discount idle time.  The BandwidthMetrics | 
|  | // is unidirectional - it should only be used to record upload or download | 
|  | // bandwidth, but not both. | 
|  | // | 
|  | // Note, the easiest thing to do is to just measure each stream and average | 
|  | // them or add them.  However, this does not work.  If multiple streams are in | 
|  | // progress concurrently, you have to look at the aggregate bandwidth at any | 
|  | // point in time. | 
|  | // | 
|  | //   Example: | 
|  | //      Imagine 4 streams opening and closing with overlapping time. | 
|  | //      We can't measure bandwidth by looking at any individual stream. | 
|  | //      We can only measure actual bandwidth by looking at the bandwidth | 
|  | //      across all open streams. | 
|  | // | 
|  | //         Time ---------------------------------------> | 
|  | //         s1 +----------------+ | 
|  | //         s2              +----------------+ | 
|  | //         s3                            +--------------+ | 
|  | //         s4                            +--------------+ | 
|  | // | 
|  | // Example usage: | 
|  | // | 
|  | //   BandwidthMetrics tracker; | 
|  | // | 
|  | //   // When a stream is created | 
|  | //   tracker.StartStream(); | 
|  | // | 
|  | //   // When data is transferred on any stream | 
|  | //   tracker.RecordSample(bytes); | 
|  | // | 
|  | //   // When the stream is finished | 
|  | //   tracker.StopStream(); | 
|  | // | 
|  | // NOTE: This class is not thread safe. | 
|  | // | 
|  | class BandwidthMetrics { | 
|  | public: | 
|  | BandwidthMetrics() | 
|  | : num_streams_in_progress_(0), | 
|  | num_data_samples_(0), | 
|  | data_sum_(0.0), | 
|  | bytes_since_last_start_(0) { | 
|  | } | 
|  |  | 
|  | // Get the bandwidth.  Returns Kbps (kilo-bits-per-second). | 
|  | double bandwidth() const { | 
|  | return data_sum_ / num_data_samples_; | 
|  | } | 
|  |  | 
|  | // Record that we've started a stream. | 
|  | void StartStream() { | 
|  | // If we're the only stream, we've finished some idle time.  Record a new | 
|  | // timestamp to indicate the start of data flow. | 
|  | if (++num_streams_in_progress_ == 1) { | 
|  | last_start_ = base::TimeTicks::HighResNow(); | 
|  | bytes_since_last_start_ = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Track that we've completed a stream. | 
|  | void StopStream() { | 
|  | if (--num_streams_in_progress_ == 0) { | 
|  | // We don't use small streams when tracking bandwidth because they are not | 
|  | // precise; imagine a 25 byte stream.  The sample is too small to make | 
|  | // a good measurement. | 
|  | // 20KB is an arbitrary value.  We might want to use a lesser value. | 
|  | static const int64 kRecordSizeThreshold = 20 * 1024; | 
|  | if (bytes_since_last_start_ < kRecordSizeThreshold) | 
|  | return; | 
|  |  | 
|  | base::TimeDelta delta = base::TimeTicks::HighResNow() - last_start_; | 
|  | double ms = delta.InMillisecondsF(); | 
|  | if (ms > 0.0) { | 
|  | double kbps = static_cast<double>(bytes_since_last_start_) * 8 / ms; | 
|  | ++num_data_samples_; | 
|  | data_sum_ += kbps; | 
|  | VLOG(1) << "Bandwidth: " << kbps | 
|  | << "Kbps (avg " << bandwidth() << "Kbps)"; | 
|  | int kbps_int = static_cast<int>(kbps); | 
|  | UMA_HISTOGRAM_COUNTS_10000("Net.DownloadBandwidth", kbps_int); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Add a sample of the number of bytes read from the network into the tracker. | 
|  | void RecordBytes(int bytes) { | 
|  | DCHECK(num_streams_in_progress_); | 
|  | bytes_since_last_start_ += static_cast<int64>(bytes); | 
|  | } | 
|  |  | 
|  | private: | 
|  | int num_streams_in_progress_;   // The number of streams in progress. | 
|  | // TODO(mbelshe): Use a rolling buffer of 30 samples instead of an average. | 
|  | int num_data_samples_;          // The number of samples collected. | 
|  | double data_sum_;               // The sum of all samples collected. | 
|  | int64 bytes_since_last_start_;  // Bytes tracked during this "session". | 
|  | base::TimeTicks last_start_;    // Timestamp of the begin of this "session". | 
|  | }; | 
|  |  | 
|  | // A utility class for managing the lifecycle of a measured stream. | 
|  | // It is important that we not leave unclosed streams, and this class helps | 
|  | // ensure we always stop them. | 
|  | class ScopedBandwidthMetrics { | 
|  | public: | 
|  | ScopedBandwidthMetrics(); | 
|  | ~ScopedBandwidthMetrics(); | 
|  |  | 
|  | void StartStream(); | 
|  | void StopStream(); | 
|  | void RecordBytes(int bytes); | 
|  |  | 
|  | private: | 
|  | bool started_; | 
|  | }; | 
|  |  | 
|  | }  // namespace net | 
|  |  | 
|  | #endif  // NET_BASE_BANDWIDTH_METRICS_H_ |