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