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