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#ifndef NET_BASE_BANDWIDTH_METRICS_H_
6#define NET_BASE_BANDWIDTH_METRICS_H_
7
8#include <list>
9
10#include "base/logging.h"
11#include "base/metrics/histogram.h"
12#include "base/time/time.h"
13
14namespace net {
15
16// Tracks statistics about the bandwidth metrics over time.  In order to
17// measure, this class needs to know when individual streams are in progress,
18// so that it can know when to discount idle time.  The BandwidthMetrics
19// is unidirectional - it should only be used to record upload or download
20// bandwidth, but not both.
21//
22// Note, the easiest thing to do is to just measure each stream and average
23// them or add them.  However, this does not work.  If multiple streams are in
24// progress concurrently, you have to look at the aggregate bandwidth at any
25// point in time.
26//
27//   Example:
28//      Imagine 4 streams opening and closing with overlapping time.
29//      We can't measure bandwidth by looking at any individual stream.
30//      We can only measure actual bandwidth by looking at the bandwidth
31//      across all open streams.
32//
33//         Time --------------------------------------->
34//         s1 +----------------+
35//         s2              +----------------+
36//         s3                            +--------------+
37//         s4                            +--------------+
38//
39// Example usage:
40//
41//   BandwidthMetrics tracker;
42//
43//   // When a stream is created
44//   tracker.StartStream();
45//
46//   // When data is transferred on any stream
47//   tracker.RecordSample(bytes);
48//
49//   // When the stream is finished
50//   tracker.StopStream();
51//
52// NOTE: This class is not thread safe.
53//
54class BandwidthMetrics {
55 public:
56  BandwidthMetrics()
57      : num_streams_in_progress_(0),
58        num_data_samples_(0),
59        data_sum_(0.0),
60        bytes_since_last_start_(0) {
61  }
62
63  // Get the bandwidth.  Returns Kbps (kilo-bits-per-second).
64  double bandwidth() const {
65    return data_sum_ / num_data_samples_;
66  }
67
68  // Record that we've started a stream.
69  void StartStream() {
70    // If we're the only stream, we've finished some idle time.  Record a new
71    // timestamp to indicate the start of data flow.
72    if (++num_streams_in_progress_ == 1) {
73      last_start_ = base::TimeTicks::HighResNow();
74      bytes_since_last_start_ = 0;
75    }
76  }
77
78  // Track that we've completed a stream.
79  void StopStream() {
80    if (--num_streams_in_progress_ == 0) {
81      // We don't use small streams when tracking bandwidth because they are not
82      // precise; imagine a 25 byte stream.  The sample is too small to make
83      // a good measurement.
84      // 20KB is an arbitrary value.  We might want to use a lesser value.
85      static const int64 kRecordSizeThreshold = 20 * 1024;
86      if (bytes_since_last_start_ < kRecordSizeThreshold)
87        return;
88
89      base::TimeDelta delta = base::TimeTicks::HighResNow() - last_start_;
90      double ms = delta.InMillisecondsF();
91      if (ms > 0.0) {
92        double kbps = static_cast<double>(bytes_since_last_start_) * 8 / ms;
93        ++num_data_samples_;
94        data_sum_ += kbps;
95        VLOG(1) << "Bandwidth: " << kbps
96                << "Kbps (avg " << bandwidth() << "Kbps)";
97        int kbps_int = static_cast<int>(kbps);
98        UMA_HISTOGRAM_COUNTS_10000("Net.DownloadBandwidth", kbps_int);
99      }
100    }
101  }
102
103  // Add a sample of the number of bytes read from the network into the tracker.
104  void RecordBytes(int bytes) {
105    DCHECK(num_streams_in_progress_);
106    bytes_since_last_start_ += static_cast<int64>(bytes);
107  }
108
109 private:
110  int num_streams_in_progress_;   // The number of streams in progress.
111  // TODO(mbelshe): Use a rolling buffer of 30 samples instead of an average.
112  int num_data_samples_;          // The number of samples collected.
113  double data_sum_;               // The sum of all samples collected.
114  int64 bytes_since_last_start_;  // Bytes tracked during this "session".
115  base::TimeTicks last_start_;    // Timestamp of the begin of this "session".
116};
117
118// A utility class for managing the lifecycle of a measured stream.
119// It is important that we not leave unclosed streams, and this class helps
120// ensure we always stop them.
121class ScopedBandwidthMetrics {
122 public:
123  ScopedBandwidthMetrics();
124  ~ScopedBandwidthMetrics();
125
126  void StartStream();
127  void StopStream();
128  void RecordBytes(int bytes);
129
130 private:
131  bool started_;
132};
133
134}  // namespace net
135
136#endif  // NET_BASE_BANDWIDTH_METRICS_H_
137