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