1*6777b538SAndroid Build Coastguard Worker // Copyright 2016 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker
5*6777b538SAndroid Build Coastguard Worker #include "net/nqe/throughput_analyzer.h"
6*6777b538SAndroid Build Coastguard Worker
7*6777b538SAndroid Build Coastguard Worker #include <cmath>
8*6777b538SAndroid Build Coastguard Worker
9*6777b538SAndroid Build Coastguard Worker #include "base/functional/bind.h"
10*6777b538SAndroid Build Coastguard Worker #include "base/location.h"
11*6777b538SAndroid Build Coastguard Worker #include "base/metrics/histogram_macros.h"
12*6777b538SAndroid Build Coastguard Worker #include "base/numerics/safe_conversions.h"
13*6777b538SAndroid Build Coastguard Worker #include "base/task/single_thread_task_runner.h"
14*6777b538SAndroid Build Coastguard Worker #include "base/time/tick_clock.h"
15*6777b538SAndroid Build Coastguard Worker #include "net/base/host_port_pair.h"
16*6777b538SAndroid Build Coastguard Worker #include "net/base/network_activity_monitor.h"
17*6777b538SAndroid Build Coastguard Worker #include "net/base/url_util.h"
18*6777b538SAndroid Build Coastguard Worker #include "net/nqe/network_quality_estimator.h"
19*6777b538SAndroid Build Coastguard Worker #include "net/nqe/network_quality_estimator_params.h"
20*6777b538SAndroid Build Coastguard Worker #include "net/nqe/network_quality_estimator_util.h"
21*6777b538SAndroid Build Coastguard Worker #include "net/url_request/url_request.h"
22*6777b538SAndroid Build Coastguard Worker #include "net/url_request/url_request_context.h"
23*6777b538SAndroid Build Coastguard Worker
24*6777b538SAndroid Build Coastguard Worker namespace net {
25*6777b538SAndroid Build Coastguard Worker
26*6777b538SAndroid Build Coastguard Worker class HostResolver;
27*6777b538SAndroid Build Coastguard Worker
28*6777b538SAndroid Build Coastguard Worker namespace {
29*6777b538SAndroid Build Coastguard Worker
30*6777b538SAndroid Build Coastguard Worker // Maximum number of accuracy degrading requests, and requests that do not
31*6777b538SAndroid Build Coastguard Worker // degrade accuracy held in the memory.
32*6777b538SAndroid Build Coastguard Worker static const size_t kMaxRequestsSize = 300;
33*6777b538SAndroid Build Coastguard Worker
34*6777b538SAndroid Build Coastguard Worker // Returns true if the request should be discarded because it does not provide
35*6777b538SAndroid Build Coastguard Worker // meaningful observation.
ShouldDiscardRequest(const URLRequest & request)36*6777b538SAndroid Build Coastguard Worker bool ShouldDiscardRequest(const URLRequest& request) {
37*6777b538SAndroid Build Coastguard Worker return request.method() != "GET";
38*6777b538SAndroid Build Coastguard Worker }
39*6777b538SAndroid Build Coastguard Worker
40*6777b538SAndroid Build Coastguard Worker } // namespace
41*6777b538SAndroid Build Coastguard Worker
42*6777b538SAndroid Build Coastguard Worker namespace nqe::internal {
43*6777b538SAndroid Build Coastguard Worker // The default content size of a HTML response body. It is set to the median
44*6777b538SAndroid Build Coastguard Worker // HTML response content size, i.e. 1.8kB.
45*6777b538SAndroid Build Coastguard Worker constexpr int64_t kDefaultContentSizeBytes = 1800;
46*6777b538SAndroid Build Coastguard Worker
ThroughputAnalyzer(const NetworkQualityEstimator * network_quality_estimator,const NetworkQualityEstimatorParams * params,scoped_refptr<base::SingleThreadTaskRunner> task_runner,ThroughputObservationCallback throughput_observation_callback,const base::TickClock * tick_clock,const NetLogWithSource & net_log)47*6777b538SAndroid Build Coastguard Worker ThroughputAnalyzer::ThroughputAnalyzer(
48*6777b538SAndroid Build Coastguard Worker const NetworkQualityEstimator* network_quality_estimator,
49*6777b538SAndroid Build Coastguard Worker const NetworkQualityEstimatorParams* params,
50*6777b538SAndroid Build Coastguard Worker scoped_refptr<base::SingleThreadTaskRunner> task_runner,
51*6777b538SAndroid Build Coastguard Worker ThroughputObservationCallback throughput_observation_callback,
52*6777b538SAndroid Build Coastguard Worker const base::TickClock* tick_clock,
53*6777b538SAndroid Build Coastguard Worker const NetLogWithSource& net_log)
54*6777b538SAndroid Build Coastguard Worker : network_quality_estimator_(network_quality_estimator),
55*6777b538SAndroid Build Coastguard Worker params_(params),
56*6777b538SAndroid Build Coastguard Worker task_runner_(task_runner),
57*6777b538SAndroid Build Coastguard Worker throughput_observation_callback_(throughput_observation_callback),
58*6777b538SAndroid Build Coastguard Worker tick_clock_(tick_clock),
59*6777b538SAndroid Build Coastguard Worker last_connection_change_(tick_clock_->NowTicks()),
60*6777b538SAndroid Build Coastguard Worker window_start_time_(base::TimeTicks()),
61*6777b538SAndroid Build Coastguard Worker net_log_(net_log) {
62*6777b538SAndroid Build Coastguard Worker DCHECK(tick_clock_);
63*6777b538SAndroid Build Coastguard Worker DCHECK(network_quality_estimator_);
64*6777b538SAndroid Build Coastguard Worker DCHECK(params_);
65*6777b538SAndroid Build Coastguard Worker DCHECK(task_runner_);
66*6777b538SAndroid Build Coastguard Worker DCHECK(!IsCurrentlyTrackingThroughput());
67*6777b538SAndroid Build Coastguard Worker }
68*6777b538SAndroid Build Coastguard Worker
~ThroughputAnalyzer()69*6777b538SAndroid Build Coastguard Worker ThroughputAnalyzer::~ThroughputAnalyzer() {
70*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
71*6777b538SAndroid Build Coastguard Worker }
72*6777b538SAndroid Build Coastguard Worker
MaybeStartThroughputObservationWindow()73*6777b538SAndroid Build Coastguard Worker void ThroughputAnalyzer::MaybeStartThroughputObservationWindow() {
74*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
75*6777b538SAndroid Build Coastguard Worker
76*6777b538SAndroid Build Coastguard Worker if (disable_throughput_measurements_)
77*6777b538SAndroid Build Coastguard Worker return;
78*6777b538SAndroid Build Coastguard Worker
79*6777b538SAndroid Build Coastguard Worker // Throughput observation window can be started only if no accuracy degrading
80*6777b538SAndroid Build Coastguard Worker // requests are currently active, the observation window is not already
81*6777b538SAndroid Build Coastguard Worker // started, and there is at least one active request that does not degrade
82*6777b538SAndroid Build Coastguard Worker // throughput computation accuracy.
83*6777b538SAndroid Build Coastguard Worker if (accuracy_degrading_requests_.size() > 0 ||
84*6777b538SAndroid Build Coastguard Worker IsCurrentlyTrackingThroughput() ||
85*6777b538SAndroid Build Coastguard Worker requests_.size() < params_->throughput_min_requests_in_flight()) {
86*6777b538SAndroid Build Coastguard Worker return;
87*6777b538SAndroid Build Coastguard Worker }
88*6777b538SAndroid Build Coastguard Worker window_start_time_ = tick_clock_->NowTicks();
89*6777b538SAndroid Build Coastguard Worker bits_received_at_window_start_ = GetBitsReceived();
90*6777b538SAndroid Build Coastguard Worker }
91*6777b538SAndroid Build Coastguard Worker
EndThroughputObservationWindow()92*6777b538SAndroid Build Coastguard Worker void ThroughputAnalyzer::EndThroughputObservationWindow() {
93*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
94*6777b538SAndroid Build Coastguard Worker
95*6777b538SAndroid Build Coastguard Worker // Mark the throughput observation window as stopped by resetting the window
96*6777b538SAndroid Build Coastguard Worker // parameters.
97*6777b538SAndroid Build Coastguard Worker window_start_time_ = base::TimeTicks();
98*6777b538SAndroid Build Coastguard Worker bits_received_at_window_start_ = 0;
99*6777b538SAndroid Build Coastguard Worker DCHECK(!IsCurrentlyTrackingThroughput());
100*6777b538SAndroid Build Coastguard Worker }
101*6777b538SAndroid Build Coastguard Worker
IsCurrentlyTrackingThroughput() const102*6777b538SAndroid Build Coastguard Worker bool ThroughputAnalyzer::IsCurrentlyTrackingThroughput() const {
103*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
104*6777b538SAndroid Build Coastguard Worker
105*6777b538SAndroid Build Coastguard Worker if (window_start_time_.is_null())
106*6777b538SAndroid Build Coastguard Worker return false;
107*6777b538SAndroid Build Coastguard Worker
108*6777b538SAndroid Build Coastguard Worker // If the throughput observation window is running, then at least one request
109*6777b538SAndroid Build Coastguard Worker // that does not degrade throughput computation accuracy should be active.
110*6777b538SAndroid Build Coastguard Worker DCHECK_GT(requests_.size(), 0U);
111*6777b538SAndroid Build Coastguard Worker
112*6777b538SAndroid Build Coastguard Worker // If the throughput observation window is running, then no accuracy degrading
113*6777b538SAndroid Build Coastguard Worker // requests should be currently active.
114*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(0U, accuracy_degrading_requests_.size());
115*6777b538SAndroid Build Coastguard Worker
116*6777b538SAndroid Build Coastguard Worker DCHECK_LE(params_->throughput_min_requests_in_flight(), requests_.size());
117*6777b538SAndroid Build Coastguard Worker
118*6777b538SAndroid Build Coastguard Worker return true;
119*6777b538SAndroid Build Coastguard Worker }
120*6777b538SAndroid Build Coastguard Worker
SetTickClockForTesting(const base::TickClock * tick_clock)121*6777b538SAndroid Build Coastguard Worker void ThroughputAnalyzer::SetTickClockForTesting(
122*6777b538SAndroid Build Coastguard Worker const base::TickClock* tick_clock) {
123*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
124*6777b538SAndroid Build Coastguard Worker tick_clock_ = tick_clock;
125*6777b538SAndroid Build Coastguard Worker DCHECK(tick_clock_);
126*6777b538SAndroid Build Coastguard Worker }
127*6777b538SAndroid Build Coastguard Worker
UpdateResponseContentSize(const URLRequest * request,int64_t response_size)128*6777b538SAndroid Build Coastguard Worker void ThroughputAnalyzer::UpdateResponseContentSize(const URLRequest* request,
129*6777b538SAndroid Build Coastguard Worker int64_t response_size) {
130*6777b538SAndroid Build Coastguard Worker DCHECK_LE(0, response_size);
131*6777b538SAndroid Build Coastguard Worker // Updates the map and the counter. Subtracts the previous stored response
132*6777b538SAndroid Build Coastguard Worker // content size if an old record exists in the map.
133*6777b538SAndroid Build Coastguard Worker if (response_content_sizes_.find(request) != response_content_sizes_.end()) {
134*6777b538SAndroid Build Coastguard Worker total_response_content_size_ +=
135*6777b538SAndroid Build Coastguard Worker response_size - response_content_sizes_[request];
136*6777b538SAndroid Build Coastguard Worker } else {
137*6777b538SAndroid Build Coastguard Worker total_response_content_size_ += response_size;
138*6777b538SAndroid Build Coastguard Worker }
139*6777b538SAndroid Build Coastguard Worker response_content_sizes_[request] = response_size;
140*6777b538SAndroid Build Coastguard Worker }
141*6777b538SAndroid Build Coastguard Worker
NotifyStartTransaction(const URLRequest & request)142*6777b538SAndroid Build Coastguard Worker void ThroughputAnalyzer::NotifyStartTransaction(const URLRequest& request) {
143*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
144*6777b538SAndroid Build Coastguard Worker
145*6777b538SAndroid Build Coastguard Worker UpdateResponseContentSize(&request, kDefaultContentSizeBytes);
146*6777b538SAndroid Build Coastguard Worker
147*6777b538SAndroid Build Coastguard Worker if (disable_throughput_measurements_)
148*6777b538SAndroid Build Coastguard Worker return;
149*6777b538SAndroid Build Coastguard Worker
150*6777b538SAndroid Build Coastguard Worker const bool degrades_accuracy = DegradesAccuracy(request);
151*6777b538SAndroid Build Coastguard Worker if (degrades_accuracy) {
152*6777b538SAndroid Build Coastguard Worker accuracy_degrading_requests_.insert(&request);
153*6777b538SAndroid Build Coastguard Worker
154*6777b538SAndroid Build Coastguard Worker BoundRequestsSize();
155*6777b538SAndroid Build Coastguard Worker
156*6777b538SAndroid Build Coastguard Worker // Call EndThroughputObservationWindow since observations cannot be
157*6777b538SAndroid Build Coastguard Worker // recorded in the presence of requests that degrade throughput computation
158*6777b538SAndroid Build Coastguard Worker // accuracy.
159*6777b538SAndroid Build Coastguard Worker EndThroughputObservationWindow();
160*6777b538SAndroid Build Coastguard Worker DCHECK(!IsCurrentlyTrackingThroughput());
161*6777b538SAndroid Build Coastguard Worker return;
162*6777b538SAndroid Build Coastguard Worker } else if (ShouldDiscardRequest(request)) {
163*6777b538SAndroid Build Coastguard Worker return;
164*6777b538SAndroid Build Coastguard Worker }
165*6777b538SAndroid Build Coastguard Worker
166*6777b538SAndroid Build Coastguard Worker EraseHangingRequests(request);
167*6777b538SAndroid Build Coastguard Worker
168*6777b538SAndroid Build Coastguard Worker requests_[&request] = tick_clock_->NowTicks();
169*6777b538SAndroid Build Coastguard Worker BoundRequestsSize();
170*6777b538SAndroid Build Coastguard Worker MaybeStartThroughputObservationWindow();
171*6777b538SAndroid Build Coastguard Worker }
172*6777b538SAndroid Build Coastguard Worker
NotifyBytesRead(const URLRequest & request)173*6777b538SAndroid Build Coastguard Worker void ThroughputAnalyzer::NotifyBytesRead(const URLRequest& request) {
174*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
175*6777b538SAndroid Build Coastguard Worker
176*6777b538SAndroid Build Coastguard Worker if (disable_throughput_measurements_)
177*6777b538SAndroid Build Coastguard Worker return;
178*6777b538SAndroid Build Coastguard Worker
179*6777b538SAndroid Build Coastguard Worker EraseHangingRequests(request);
180*6777b538SAndroid Build Coastguard Worker
181*6777b538SAndroid Build Coastguard Worker if (requests_.erase(&request) == 0)
182*6777b538SAndroid Build Coastguard Worker return;
183*6777b538SAndroid Build Coastguard Worker
184*6777b538SAndroid Build Coastguard Worker // Update the time when the bytes were received for |request|.
185*6777b538SAndroid Build Coastguard Worker requests_[&request] = tick_clock_->NowTicks();
186*6777b538SAndroid Build Coastguard Worker }
187*6777b538SAndroid Build Coastguard Worker
NotifyRequestCompleted(const URLRequest & request)188*6777b538SAndroid Build Coastguard Worker void ThroughputAnalyzer::NotifyRequestCompleted(const URLRequest& request) {
189*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
190*6777b538SAndroid Build Coastguard Worker
191*6777b538SAndroid Build Coastguard Worker // Remove the request from the inflight requests if it presents in the map.
192*6777b538SAndroid Build Coastguard Worker if (response_content_sizes_.find(&request) != response_content_sizes_.end()) {
193*6777b538SAndroid Build Coastguard Worker total_response_content_size_ -= response_content_sizes_[&request];
194*6777b538SAndroid Build Coastguard Worker response_content_sizes_.erase(&request);
195*6777b538SAndroid Build Coastguard Worker }
196*6777b538SAndroid Build Coastguard Worker
197*6777b538SAndroid Build Coastguard Worker if (disable_throughput_measurements_)
198*6777b538SAndroid Build Coastguard Worker return;
199*6777b538SAndroid Build Coastguard Worker
200*6777b538SAndroid Build Coastguard Worker // Return early if the |request| is not present in the collections of
201*6777b538SAndroid Build Coastguard Worker // requests. This may happen when a completed request is later destroyed.
202*6777b538SAndroid Build Coastguard Worker if (requests_.find(&request) == requests_.end() &&
203*6777b538SAndroid Build Coastguard Worker accuracy_degrading_requests_.find(&request) ==
204*6777b538SAndroid Build Coastguard Worker accuracy_degrading_requests_.end()) {
205*6777b538SAndroid Build Coastguard Worker return;
206*6777b538SAndroid Build Coastguard Worker }
207*6777b538SAndroid Build Coastguard Worker
208*6777b538SAndroid Build Coastguard Worker EraseHangingRequests(request);
209*6777b538SAndroid Build Coastguard Worker
210*6777b538SAndroid Build Coastguard Worker int32_t downstream_kbps = -1;
211*6777b538SAndroid Build Coastguard Worker if (MaybeGetThroughputObservation(&downstream_kbps)) {
212*6777b538SAndroid Build Coastguard Worker // Notify the provided callback.
213*6777b538SAndroid Build Coastguard Worker task_runner_->PostTask(
214*6777b538SAndroid Build Coastguard Worker FROM_HERE,
215*6777b538SAndroid Build Coastguard Worker base::BindOnce(throughput_observation_callback_, downstream_kbps));
216*6777b538SAndroid Build Coastguard Worker }
217*6777b538SAndroid Build Coastguard Worker
218*6777b538SAndroid Build Coastguard Worker // Try to remove the request from either |accuracy_degrading_requests_| or
219*6777b538SAndroid Build Coastguard Worker // |requests_|, since it is no longer active.
220*6777b538SAndroid Build Coastguard Worker if (accuracy_degrading_requests_.erase(&request) == 1u) {
221*6777b538SAndroid Build Coastguard Worker // Generally, |request| cannot be in both |accuracy_degrading_requests_|
222*6777b538SAndroid Build Coastguard Worker // and |requests_| at the same time. However, in some cases, the same
223*6777b538SAndroid Build Coastguard Worker // request may appear in both vectors. See https://crbug.com/849604 for
224*6777b538SAndroid Build Coastguard Worker // more details.
225*6777b538SAndroid Build Coastguard Worker // It's safe to delete |request| from |requests_| since (i)
226*6777b538SAndroid Build Coastguard Worker // The observation window is currently not recording throughput, and (ii)
227*6777b538SAndroid Build Coastguard Worker // |requests_| is a best effort guess of requests that are currently
228*6777b538SAndroid Build Coastguard Worker // in-flight.
229*6777b538SAndroid Build Coastguard Worker DCHECK(!IsCurrentlyTrackingThroughput());
230*6777b538SAndroid Build Coastguard Worker requests_.erase(&request);
231*6777b538SAndroid Build Coastguard Worker
232*6777b538SAndroid Build Coastguard Worker // If a request that degraded the accuracy of throughput computation has
233*6777b538SAndroid Build Coastguard Worker // completed, then it may be possible to start the tracking window.
234*6777b538SAndroid Build Coastguard Worker MaybeStartThroughputObservationWindow();
235*6777b538SAndroid Build Coastguard Worker return;
236*6777b538SAndroid Build Coastguard Worker }
237*6777b538SAndroid Build Coastguard Worker
238*6777b538SAndroid Build Coastguard Worker if (requests_.erase(&request) == 1u) {
239*6777b538SAndroid Build Coastguard Worker // If there is no network activity, stop tracking throughput to prevent
240*6777b538SAndroid Build Coastguard Worker // recording of any observations.
241*6777b538SAndroid Build Coastguard Worker if (requests_.size() < params_->throughput_min_requests_in_flight())
242*6777b538SAndroid Build Coastguard Worker EndThroughputObservationWindow();
243*6777b538SAndroid Build Coastguard Worker return;
244*6777b538SAndroid Build Coastguard Worker }
245*6777b538SAndroid Build Coastguard Worker MaybeStartThroughputObservationWindow();
246*6777b538SAndroid Build Coastguard Worker }
247*6777b538SAndroid Build Coastguard Worker
NotifyExpectedResponseContentSize(const URLRequest & request,int64_t expected_content_size)248*6777b538SAndroid Build Coastguard Worker void ThroughputAnalyzer::NotifyExpectedResponseContentSize(
249*6777b538SAndroid Build Coastguard Worker const URLRequest& request,
250*6777b538SAndroid Build Coastguard Worker int64_t expected_content_size) {
251*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
252*6777b538SAndroid Build Coastguard Worker // Updates when the value is valid.
253*6777b538SAndroid Build Coastguard Worker if (expected_content_size >= 0) {
254*6777b538SAndroid Build Coastguard Worker UpdateResponseContentSize(&request, expected_content_size);
255*6777b538SAndroid Build Coastguard Worker }
256*6777b538SAndroid Build Coastguard Worker }
257*6777b538SAndroid Build Coastguard Worker
IsHangingWindow(int64_t bits_received,base::TimeDelta duration) const258*6777b538SAndroid Build Coastguard Worker bool ThroughputAnalyzer::IsHangingWindow(int64_t bits_received,
259*6777b538SAndroid Build Coastguard Worker base::TimeDelta duration) const {
260*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
261*6777b538SAndroid Build Coastguard Worker
262*6777b538SAndroid Build Coastguard Worker if (params_->throughput_hanging_requests_cwnd_size_multiplier() <= 0)
263*6777b538SAndroid Build Coastguard Worker return false;
264*6777b538SAndroid Build Coastguard Worker
265*6777b538SAndroid Build Coastguard Worker if (params_->use_small_responses())
266*6777b538SAndroid Build Coastguard Worker return false;
267*6777b538SAndroid Build Coastguard Worker
268*6777b538SAndroid Build Coastguard Worker if (!duration.is_positive())
269*6777b538SAndroid Build Coastguard Worker return false;
270*6777b538SAndroid Build Coastguard Worker
271*6777b538SAndroid Build Coastguard Worker // Initial congestion window size for TCP connections.
272*6777b538SAndroid Build Coastguard Worker static constexpr size_t kCwndSizeKilobytes = 10 * 1.5;
273*6777b538SAndroid Build Coastguard Worker static constexpr size_t kCwndSizeBits = kCwndSizeKilobytes * 1000 * 8;
274*6777b538SAndroid Build Coastguard Worker
275*6777b538SAndroid Build Coastguard Worker // Scale the |duration| to one HTTP RTT, and compute the number of bits that
276*6777b538SAndroid Build Coastguard Worker // would be received over a duration of one HTTP RTT.
277*6777b538SAndroid Build Coastguard Worker size_t bits_received_over_one_http_rtt =
278*6777b538SAndroid Build Coastguard Worker bits_received *
279*6777b538SAndroid Build Coastguard Worker (network_quality_estimator_->GetHttpRTT().value_or(base::Seconds(10)) /
280*6777b538SAndroid Build Coastguard Worker duration);
281*6777b538SAndroid Build Coastguard Worker
282*6777b538SAndroid Build Coastguard Worker // If |is_hanging| is true, it implies that less than
283*6777b538SAndroid Build Coastguard Worker // kCwndSizeKilobytes were received over a period of 1 HTTP RTT. For a network
284*6777b538SAndroid Build Coastguard Worker // that is not under-utilized, it is expected that at least |kCwndSizeBits|
285*6777b538SAndroid Build Coastguard Worker // are received over a duration of 1 HTTP RTT.
286*6777b538SAndroid Build Coastguard Worker bool is_hanging =
287*6777b538SAndroid Build Coastguard Worker bits_received_over_one_http_rtt <
288*6777b538SAndroid Build Coastguard Worker (kCwndSizeBits *
289*6777b538SAndroid Build Coastguard Worker params_->throughput_hanging_requests_cwnd_size_multiplier());
290*6777b538SAndroid Build Coastguard Worker
291*6777b538SAndroid Build Coastguard Worker return is_hanging;
292*6777b538SAndroid Build Coastguard Worker }
293*6777b538SAndroid Build Coastguard Worker
MaybeGetThroughputObservation(int32_t * downstream_kbps)294*6777b538SAndroid Build Coastguard Worker bool ThroughputAnalyzer::MaybeGetThroughputObservation(
295*6777b538SAndroid Build Coastguard Worker int32_t* downstream_kbps) {
296*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
297*6777b538SAndroid Build Coastguard Worker DCHECK(downstream_kbps);
298*6777b538SAndroid Build Coastguard Worker
299*6777b538SAndroid Build Coastguard Worker if (disable_throughput_measurements_)
300*6777b538SAndroid Build Coastguard Worker return false;
301*6777b538SAndroid Build Coastguard Worker
302*6777b538SAndroid Build Coastguard Worker // Return early if the window that records downstream throughput is currently
303*6777b538SAndroid Build Coastguard Worker // inactive because throughput observations can be taken only when the window
304*6777b538SAndroid Build Coastguard Worker // is active.
305*6777b538SAndroid Build Coastguard Worker if (!IsCurrentlyTrackingThroughput())
306*6777b538SAndroid Build Coastguard Worker return false;
307*6777b538SAndroid Build Coastguard Worker
308*6777b538SAndroid Build Coastguard Worker DCHECK_GE(requests_.size(), params_->throughput_min_requests_in_flight());
309*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(0U, accuracy_degrading_requests_.size());
310*6777b538SAndroid Build Coastguard Worker
311*6777b538SAndroid Build Coastguard Worker base::TimeTicks now = tick_clock_->NowTicks();
312*6777b538SAndroid Build Coastguard Worker
313*6777b538SAndroid Build Coastguard Worker int64_t bits_received = GetBitsReceived() - bits_received_at_window_start_;
314*6777b538SAndroid Build Coastguard Worker DCHECK_LE(window_start_time_, now);
315*6777b538SAndroid Build Coastguard Worker DCHECK_LE(0, bits_received);
316*6777b538SAndroid Build Coastguard Worker const base::TimeDelta duration = now - window_start_time_;
317*6777b538SAndroid Build Coastguard Worker
318*6777b538SAndroid Build Coastguard Worker // Ignore tiny/short transfers, which will not produce accurate rates. Skip
319*6777b538SAndroid Build Coastguard Worker // the checks if |use_small_responses_| is true.
320*6777b538SAndroid Build Coastguard Worker if (!params_->use_small_responses() &&
321*6777b538SAndroid Build Coastguard Worker bits_received < params_->GetThroughputMinTransferSizeBits()) {
322*6777b538SAndroid Build Coastguard Worker return false;
323*6777b538SAndroid Build Coastguard Worker }
324*6777b538SAndroid Build Coastguard Worker
325*6777b538SAndroid Build Coastguard Worker double downstream_kbps_double = bits_received * duration.ToHz() / 1000;
326*6777b538SAndroid Build Coastguard Worker
327*6777b538SAndroid Build Coastguard Worker if (IsHangingWindow(bits_received, duration)) {
328*6777b538SAndroid Build Coastguard Worker requests_.clear();
329*6777b538SAndroid Build Coastguard Worker EndThroughputObservationWindow();
330*6777b538SAndroid Build Coastguard Worker return false;
331*6777b538SAndroid Build Coastguard Worker }
332*6777b538SAndroid Build Coastguard Worker
333*6777b538SAndroid Build Coastguard Worker // Round-up |downstream_kbps_double|.
334*6777b538SAndroid Build Coastguard Worker *downstream_kbps = base::ClampCeil<int32_t>(downstream_kbps_double);
335*6777b538SAndroid Build Coastguard Worker DCHECK(IsCurrentlyTrackingThroughput());
336*6777b538SAndroid Build Coastguard Worker
337*6777b538SAndroid Build Coastguard Worker // Stop the observation window since a throughput measurement has been taken.
338*6777b538SAndroid Build Coastguard Worker EndThroughputObservationWindow();
339*6777b538SAndroid Build Coastguard Worker DCHECK(!IsCurrentlyTrackingThroughput());
340*6777b538SAndroid Build Coastguard Worker
341*6777b538SAndroid Build Coastguard Worker // Maybe start the throughput observation window again so that another
342*6777b538SAndroid Build Coastguard Worker // throughput measurement can be taken.
343*6777b538SAndroid Build Coastguard Worker MaybeStartThroughputObservationWindow();
344*6777b538SAndroid Build Coastguard Worker return true;
345*6777b538SAndroid Build Coastguard Worker }
346*6777b538SAndroid Build Coastguard Worker
OnConnectionTypeChanged()347*6777b538SAndroid Build Coastguard Worker void ThroughputAnalyzer::OnConnectionTypeChanged() {
348*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
349*6777b538SAndroid Build Coastguard Worker
350*6777b538SAndroid Build Coastguard Worker // All the requests that were previously not degrading the througpput
351*6777b538SAndroid Build Coastguard Worker // computation are now spanning a connection change event. These requests
352*6777b538SAndroid Build Coastguard Worker // would now degrade the throughput computation accuracy. So, move them to
353*6777b538SAndroid Build Coastguard Worker // |accuracy_degrading_requests_|.
354*6777b538SAndroid Build Coastguard Worker for (const auto& request : requests_) {
355*6777b538SAndroid Build Coastguard Worker accuracy_degrading_requests_.insert(request.first);
356*6777b538SAndroid Build Coastguard Worker }
357*6777b538SAndroid Build Coastguard Worker requests_.clear();
358*6777b538SAndroid Build Coastguard Worker BoundRequestsSize();
359*6777b538SAndroid Build Coastguard Worker EndThroughputObservationWindow();
360*6777b538SAndroid Build Coastguard Worker
361*6777b538SAndroid Build Coastguard Worker last_connection_change_ = tick_clock_->NowTicks();
362*6777b538SAndroid Build Coastguard Worker }
363*6777b538SAndroid Build Coastguard Worker
SetUseLocalHostRequestsForTesting(bool use_localhost_requests)364*6777b538SAndroid Build Coastguard Worker void ThroughputAnalyzer::SetUseLocalHostRequestsForTesting(
365*6777b538SAndroid Build Coastguard Worker bool use_localhost_requests) {
366*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
367*6777b538SAndroid Build Coastguard Worker use_localhost_requests_for_tests_ = use_localhost_requests;
368*6777b538SAndroid Build Coastguard Worker }
369*6777b538SAndroid Build Coastguard Worker
GetBitsReceived() const370*6777b538SAndroid Build Coastguard Worker int64_t ThroughputAnalyzer::GetBitsReceived() const {
371*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
372*6777b538SAndroid Build Coastguard Worker return activity_monitor::GetBytesReceived() * 8;
373*6777b538SAndroid Build Coastguard Worker }
374*6777b538SAndroid Build Coastguard Worker
CountActiveInFlightRequests() const375*6777b538SAndroid Build Coastguard Worker size_t ThroughputAnalyzer::CountActiveInFlightRequests() const {
376*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
377*6777b538SAndroid Build Coastguard Worker return requests_.size();
378*6777b538SAndroid Build Coastguard Worker }
379*6777b538SAndroid Build Coastguard Worker
CountTotalInFlightRequests() const380*6777b538SAndroid Build Coastguard Worker size_t ThroughputAnalyzer::CountTotalInFlightRequests() const {
381*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
382*6777b538SAndroid Build Coastguard Worker return response_content_sizes_.size();
383*6777b538SAndroid Build Coastguard Worker }
384*6777b538SAndroid Build Coastguard Worker
CountTotalContentSizeBytes() const385*6777b538SAndroid Build Coastguard Worker int64_t ThroughputAnalyzer::CountTotalContentSizeBytes() const {
386*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
387*6777b538SAndroid Build Coastguard Worker
388*6777b538SAndroid Build Coastguard Worker return total_response_content_size_;
389*6777b538SAndroid Build Coastguard Worker }
390*6777b538SAndroid Build Coastguard Worker
DegradesAccuracy(const URLRequest & request) const391*6777b538SAndroid Build Coastguard Worker bool ThroughputAnalyzer::DegradesAccuracy(const URLRequest& request) const {
392*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
393*6777b538SAndroid Build Coastguard Worker
394*6777b538SAndroid Build Coastguard Worker bool private_network_request =
395*6777b538SAndroid Build Coastguard Worker nqe::internal::IsRequestForPrivateHost(request, net_log_);
396*6777b538SAndroid Build Coastguard Worker
397*6777b538SAndroid Build Coastguard Worker return !(use_localhost_requests_for_tests_ || !private_network_request) ||
398*6777b538SAndroid Build Coastguard Worker request.creation_time() < last_connection_change_;
399*6777b538SAndroid Build Coastguard Worker }
400*6777b538SAndroid Build Coastguard Worker
BoundRequestsSize()401*6777b538SAndroid Build Coastguard Worker void ThroughputAnalyzer::BoundRequestsSize() {
402*6777b538SAndroid Build Coastguard Worker if (accuracy_degrading_requests_.size() > kMaxRequestsSize) {
403*6777b538SAndroid Build Coastguard Worker // Clear |accuracy_degrading_requests_| since its size has exceeded its
404*6777b538SAndroid Build Coastguard Worker // capacity.
405*6777b538SAndroid Build Coastguard Worker accuracy_degrading_requests_.clear();
406*6777b538SAndroid Build Coastguard Worker // Disable throughput measurements since |this| has lost track of the
407*6777b538SAndroid Build Coastguard Worker // accuracy degrading requests.
408*6777b538SAndroid Build Coastguard Worker disable_throughput_measurements_ = true;
409*6777b538SAndroid Build Coastguard Worker
410*6777b538SAndroid Build Coastguard Worker // Reset other variables related to tracking since the tracking is now
411*6777b538SAndroid Build Coastguard Worker // disabled.
412*6777b538SAndroid Build Coastguard Worker EndThroughputObservationWindow();
413*6777b538SAndroid Build Coastguard Worker DCHECK(!IsCurrentlyTrackingThroughput());
414*6777b538SAndroid Build Coastguard Worker requests_.clear();
415*6777b538SAndroid Build Coastguard Worker
416*6777b538SAndroid Build Coastguard Worker // TODO(tbansal): crbug.com/609174 Add UMA to record how frequently this
417*6777b538SAndroid Build Coastguard Worker // happens.
418*6777b538SAndroid Build Coastguard Worker }
419*6777b538SAndroid Build Coastguard Worker
420*6777b538SAndroid Build Coastguard Worker if (requests_.size() > kMaxRequestsSize) {
421*6777b538SAndroid Build Coastguard Worker // Clear |requests_| since its size has exceeded its capacity.
422*6777b538SAndroid Build Coastguard Worker EndThroughputObservationWindow();
423*6777b538SAndroid Build Coastguard Worker DCHECK(!IsCurrentlyTrackingThroughput());
424*6777b538SAndroid Build Coastguard Worker requests_.clear();
425*6777b538SAndroid Build Coastguard Worker
426*6777b538SAndroid Build Coastguard Worker // TODO(tbansal): crbug.com/609174 Add UMA to record how frequently this
427*6777b538SAndroid Build Coastguard Worker // happens.
428*6777b538SAndroid Build Coastguard Worker }
429*6777b538SAndroid Build Coastguard Worker }
430*6777b538SAndroid Build Coastguard Worker
EraseHangingRequests(const URLRequest & request)431*6777b538SAndroid Build Coastguard Worker void ThroughputAnalyzer::EraseHangingRequests(const URLRequest& request) {
432*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
433*6777b538SAndroid Build Coastguard Worker
434*6777b538SAndroid Build Coastguard Worker DCHECK_LT(0, params_->hanging_request_duration_http_rtt_multiplier());
435*6777b538SAndroid Build Coastguard Worker
436*6777b538SAndroid Build Coastguard Worker const base::TimeTicks now = tick_clock_->NowTicks();
437*6777b538SAndroid Build Coastguard Worker
438*6777b538SAndroid Build Coastguard Worker const base::TimeDelta http_rtt =
439*6777b538SAndroid Build Coastguard Worker network_quality_estimator_->GetHttpRTT().value_or(base::Seconds(60));
440*6777b538SAndroid Build Coastguard Worker
441*6777b538SAndroid Build Coastguard Worker size_t count_request_erased = 0;
442*6777b538SAndroid Build Coastguard Worker auto request_it = requests_.find(&request);
443*6777b538SAndroid Build Coastguard Worker if (request_it != requests_.end()) {
444*6777b538SAndroid Build Coastguard Worker base::TimeDelta time_since_last_received = now - request_it->second;
445*6777b538SAndroid Build Coastguard Worker
446*6777b538SAndroid Build Coastguard Worker if (time_since_last_received >=
447*6777b538SAndroid Build Coastguard Worker params_->hanging_request_duration_http_rtt_multiplier() *
448*6777b538SAndroid Build Coastguard Worker http_rtt &&
449*6777b538SAndroid Build Coastguard Worker time_since_last_received >= params_->hanging_request_min_duration()) {
450*6777b538SAndroid Build Coastguard Worker count_request_erased++;
451*6777b538SAndroid Build Coastguard Worker requests_.erase(request_it);
452*6777b538SAndroid Build Coastguard Worker }
453*6777b538SAndroid Build Coastguard Worker }
454*6777b538SAndroid Build Coastguard Worker
455*6777b538SAndroid Build Coastguard Worker if (now - last_hanging_request_check_ >= base::Seconds(1)) {
456*6777b538SAndroid Build Coastguard Worker // Hanging request check is done at most once per second.
457*6777b538SAndroid Build Coastguard Worker last_hanging_request_check_ = now;
458*6777b538SAndroid Build Coastguard Worker
459*6777b538SAndroid Build Coastguard Worker for (auto it = requests_.begin(); it != requests_.end();) {
460*6777b538SAndroid Build Coastguard Worker base::TimeDelta time_since_last_received = now - it->second;
461*6777b538SAndroid Build Coastguard Worker
462*6777b538SAndroid Build Coastguard Worker if (time_since_last_received >=
463*6777b538SAndroid Build Coastguard Worker params_->hanging_request_duration_http_rtt_multiplier() *
464*6777b538SAndroid Build Coastguard Worker http_rtt &&
465*6777b538SAndroid Build Coastguard Worker time_since_last_received >= params_->hanging_request_min_duration()) {
466*6777b538SAndroid Build Coastguard Worker count_request_erased++;
467*6777b538SAndroid Build Coastguard Worker requests_.erase(it++);
468*6777b538SAndroid Build Coastguard Worker } else {
469*6777b538SAndroid Build Coastguard Worker ++it;
470*6777b538SAndroid Build Coastguard Worker }
471*6777b538SAndroid Build Coastguard Worker }
472*6777b538SAndroid Build Coastguard Worker }
473*6777b538SAndroid Build Coastguard Worker
474*6777b538SAndroid Build Coastguard Worker if (count_request_erased > 0) {
475*6777b538SAndroid Build Coastguard Worker // End the observation window since there is at least one hanging GET in
476*6777b538SAndroid Build Coastguard Worker // flight, which may lead to inaccuracies in the throughput estimate
477*6777b538SAndroid Build Coastguard Worker // computation.
478*6777b538SAndroid Build Coastguard Worker EndThroughputObservationWindow();
479*6777b538SAndroid Build Coastguard Worker }
480*6777b538SAndroid Build Coastguard Worker }
481*6777b538SAndroid Build Coastguard Worker
482*6777b538SAndroid Build Coastguard Worker } // namespace nqe::internal
483*6777b538SAndroid Build Coastguard Worker
484*6777b538SAndroid Build Coastguard Worker } // namespace net
485