OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/net/predictor.h" | 5 #include "chrome/browser/net/predictor.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <cmath> | 8 #include <cmath> |
9 #include <set> | 9 #include <set> |
10 #include <sstream> | 10 #include <sstream> |
11 | 11 |
| 12 #include "base/basictypes.h" |
12 #include "base/bind.h" | 13 #include "base/bind.h" |
13 #include "base/command_line.h" | 14 #include "base/command_line.h" |
14 #include "base/compiler_specific.h" | 15 #include "base/compiler_specific.h" |
| 16 #include "base/containers/mru_cache.h" |
15 #include "base/metrics/histogram.h" | 17 #include "base/metrics/histogram.h" |
16 #include "base/prefs/pref_service.h" | 18 #include "base/prefs/pref_service.h" |
17 #include "base/stl_util.h" | 19 #include "base/stl_util.h" |
18 #include "base/strings/string_split.h" | 20 #include "base/strings/string_split.h" |
19 #include "base/strings/string_util.h" | 21 #include "base/strings/string_util.h" |
20 #include "base/strings/stringprintf.h" | 22 #include "base/strings/stringprintf.h" |
21 #include "base/synchronization/waitable_event.h" | 23 #include "base/synchronization/waitable_event.h" |
22 #include "base/threading/thread_restrictions.h" | 24 #include "base/threading/thread_restrictions.h" |
23 #include "base/time/time.h" | 25 #include "base/time/time.h" |
24 #include "base/values.h" | 26 #include "base/values.h" |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
118 | 120 |
119 Predictor* predictor_; // The predictor which started us. | 121 Predictor* predictor_; // The predictor which started us. |
120 | 122 |
121 const GURL url_; // Hostname to resolve. | 123 const GURL url_; // Hostname to resolve. |
122 net::SingleRequestHostResolver resolver_; | 124 net::SingleRequestHostResolver resolver_; |
123 net::AddressList addresses_; | 125 net::AddressList addresses_; |
124 | 126 |
125 DISALLOW_COPY_AND_ASSIGN(LookupRequest); | 127 DISALLOW_COPY_AND_ASSIGN(LookupRequest); |
126 }; | 128 }; |
127 | 129 |
| 130 // This records UMAs for preconnect usage based on navigation URLs to |
| 131 // gather precision/recall for user-event based preconnect triggers. |
| 132 // Stats are gathered via a LRU cache that remembers all preconnect within the |
| 133 // last N seconds. |
| 134 // A preconnect trigger is considered as used iff a navigation including |
| 135 // access to the preconnected host occurs within a time period specified by |
| 136 // kMaxUnusedSocketLifetimeSecondsWithoutAGet. |
| 137 class Predictor::PreconnectUsage { |
| 138 public: |
| 139 PreconnectUsage(); |
| 140 ~PreconnectUsage(); |
| 141 |
| 142 // Record a preconnect trigger to |url|. |
| 143 void ObservePreconnect(const GURL& url); |
| 144 |
| 145 // Record a user navigation with its redirect history, |url_chain|. |
| 146 // We are uncertain if this is actually a link navigation. |
| 147 void ObserveNavigationChain(const std::vector<GURL>& url_chain, |
| 148 bool is_subresource); |
| 149 |
| 150 // Record a user link navigation to |final_url|. |
| 151 // We are certain that this is a user-triggered link navigation. |
| 152 void ObserveLinkNavigation(const GURL& final_url); |
| 153 |
| 154 private: |
| 155 // This tracks whether a preconnect was used in some navigation or not |
| 156 class PreconnectPrecisionStat { |
| 157 public: |
| 158 PreconnectPrecisionStat() |
| 159 : timestamp_(base::TimeTicks::Now()), |
| 160 was_used_(false) { |
| 161 } |
| 162 |
| 163 const base::TimeTicks& timestamp() { return timestamp_; } |
| 164 |
| 165 void set_was_used() { was_used_ = true; } |
| 166 bool was_used() const { return was_used_; } |
| 167 |
| 168 private: |
| 169 base::TimeTicks timestamp_; |
| 170 bool was_used_; |
| 171 }; |
| 172 |
| 173 typedef base::MRUCache<GURL, PreconnectPrecisionStat> MRUPreconnects; |
| 174 MRUPreconnects mru_preconnects_; |
| 175 |
| 176 // The longest time an entry can persist in mru_preconnect_ |
| 177 const base::TimeDelta max_duration_; |
| 178 |
| 179 std::vector<GURL> recent_navigation_chain_; |
| 180 |
| 181 DISALLOW_COPY_AND_ASSIGN(PreconnectUsage); |
| 182 }; |
| 183 |
| 184 Predictor::PreconnectUsage::PreconnectUsage() |
| 185 : mru_preconnects_(MRUPreconnects::NO_AUTO_EVICT), |
| 186 max_duration_(base::TimeDelta::FromSeconds( |
| 187 Predictor::kMaxUnusedSocketLifetimeSecondsWithoutAGet)) { |
| 188 } |
| 189 |
| 190 Predictor::PreconnectUsage::~PreconnectUsage() {} |
| 191 |
| 192 void Predictor::PreconnectUsage::ObservePreconnect(const GURL& url) { |
| 193 // Evict any overly old entries and record stats. |
| 194 base::TimeTicks now = base::TimeTicks::Now(); |
| 195 |
| 196 MRUPreconnects::reverse_iterator eldest_preconnect = |
| 197 mru_preconnects_.rbegin(); |
| 198 while (!mru_preconnects_.empty()) { |
| 199 DCHECK(eldest_preconnect == mru_preconnects_.rbegin()); |
| 200 if (now - eldest_preconnect->second.timestamp() < max_duration_) |
| 201 break; |
| 202 |
| 203 UMA_HISTOGRAM_BOOLEAN("Net.PreconnectTriggerUsed", |
| 204 eldest_preconnect->second.was_used()); |
| 205 eldest_preconnect = mru_preconnects_.Erase(eldest_preconnect); |
| 206 } |
| 207 |
| 208 // Add new entry. |
| 209 GURL canonical_url(Predictor::CanonicalizeUrl(url)); |
| 210 mru_preconnects_.Put(canonical_url, PreconnectPrecisionStat()); |
| 211 } |
| 212 |
| 213 void Predictor::PreconnectUsage::ObserveNavigationChain( |
| 214 const std::vector<GURL>& url_chain, |
| 215 bool is_subresource) { |
| 216 if (url_chain.empty()) |
| 217 return; |
| 218 |
| 219 if (!is_subresource) |
| 220 recent_navigation_chain_ = url_chain; |
| 221 |
| 222 GURL canonical_url(Predictor::CanonicalizeUrl(url_chain.back())); |
| 223 |
| 224 // Record the preconnect trigger for the url as used if exist |
| 225 MRUPreconnects::iterator itPreconnect = mru_preconnects_.Peek(canonical_url); |
| 226 bool was_preconnected = (itPreconnect != mru_preconnects_.end()); |
| 227 if (was_preconnected) |
| 228 itPreconnect->second.set_was_used(); |
| 229 |
| 230 // This is an UMA which was named incorrectly. This actually measures the |
| 231 // ratio of URLRequests which have used a preconnected session. |
| 232 UMA_HISTOGRAM_BOOLEAN("Net.PreconnectedNavigation", was_preconnected); |
| 233 } |
| 234 |
| 235 void Predictor::PreconnectUsage::ObserveLinkNavigation(const GURL& url) { |
| 236 if (recent_navigation_chain_.empty() || |
| 237 url != recent_navigation_chain_.back()) { |
| 238 // The navigation chain is not available for this navigation. |
| 239 recent_navigation_chain_.clear(); |
| 240 recent_navigation_chain_.push_back(url); |
| 241 } |
| 242 |
| 243 // See if the link navigation involved preconnected session. |
| 244 bool did_use_preconnect = false; |
| 245 for (std::vector<GURL>::const_iterator it = recent_navigation_chain_.begin(); |
| 246 it != recent_navigation_chain_.end(); |
| 247 ++it) { |
| 248 GURL canonical_url(Predictor::CanonicalizeUrl(*it)); |
| 249 |
| 250 // Record the preconnect trigger for the url as used if exist |
| 251 MRUPreconnects::iterator itPreconnect = |
| 252 mru_preconnects_.Peek(canonical_url); |
| 253 bool was_preconnected = (itPreconnect != mru_preconnects_.end()); |
| 254 if (was_preconnected) |
| 255 did_use_preconnect = true; |
| 256 } |
| 257 |
| 258 UMA_HISTOGRAM_BOOLEAN("Net.PreconnectedLinkNavigations", did_use_preconnect); |
| 259 } |
| 260 |
128 Predictor::Predictor(bool preconnect_enabled) | 261 Predictor::Predictor(bool preconnect_enabled) |
129 : url_request_context_getter_(NULL), | 262 : url_request_context_getter_(NULL), |
130 predictor_enabled_(true), | 263 predictor_enabled_(true), |
131 peak_pending_lookups_(0), | 264 peak_pending_lookups_(0), |
132 shutdown_(false), | 265 shutdown_(false), |
133 max_concurrent_dns_lookups_(g_max_parallel_resolves), | 266 max_concurrent_dns_lookups_(g_max_parallel_resolves), |
134 max_dns_queue_delay_( | 267 max_dns_queue_delay_( |
135 TimeDelta::FromMilliseconds(g_max_queueing_delay_ms)), | 268 TimeDelta::FromMilliseconds(g_max_queueing_delay_ms)), |
136 host_resolver_(NULL), | 269 host_resolver_(NULL), |
137 preconnect_enabled_(preconnect_enabled), | 270 preconnect_enabled_(preconnect_enabled), |
138 consecutive_omnibox_preconnect_count_(0), | 271 consecutive_omnibox_preconnect_count_(0), |
139 recent_preconnects_( | |
140 TimeDelta::FromSeconds(kMaxUnusedSocketLifetimeSecondsWithoutAGet)), | |
141 next_trim_time_(base::TimeTicks::Now() + | 272 next_trim_time_(base::TimeTicks::Now() + |
142 TimeDelta::FromHours(kDurationBetweenTrimmingsHours)) { | 273 TimeDelta::FromHours(kDurationBetweenTrimmingsHours)) { |
143 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 274 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
144 } | 275 } |
145 | 276 |
146 Predictor::~Predictor() { | 277 Predictor::~Predictor() { |
147 // TODO(rlp): Add DCHECK for CurrentlyOn(BrowserThread::IO) when the | 278 // TODO(rlp): Add DCHECK for CurrentlyOn(BrowserThread::IO) when the |
148 // ProfileManagerTest has been updated with a mock profile. | 279 // ProfileManagerTest has been updated with a mock profile. |
149 DCHECK(shutdown_); | 280 DCHECK(shutdown_); |
150 } | 281 } |
(...skipping 510 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
661 void Predictor::FinalizeInitializationOnIOThread( | 792 void Predictor::FinalizeInitializationOnIOThread( |
662 const UrlList& startup_urls, | 793 const UrlList& startup_urls, |
663 base::ListValue* referral_list, | 794 base::ListValue* referral_list, |
664 IOThread* io_thread, | 795 IOThread* io_thread, |
665 bool predictor_enabled) { | 796 bool predictor_enabled) { |
666 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 797 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
667 | 798 |
668 predictor_enabled_ = predictor_enabled; | 799 predictor_enabled_ = predictor_enabled; |
669 initial_observer_.reset(new InitialObserver()); | 800 initial_observer_.reset(new InitialObserver()); |
670 host_resolver_ = io_thread->globals()->host_resolver.get(); | 801 host_resolver_ = io_thread->globals()->host_resolver.get(); |
| 802 preconnect_usage_.reset(new PreconnectUsage()); |
671 | 803 |
672 // base::WeakPtrFactory instances need to be created and destroyed | 804 // base::WeakPtrFactory instances need to be created and destroyed |
673 // on the same thread. The predictor lives on the IO thread and will die | 805 // on the same thread. The predictor lives on the IO thread and will die |
674 // from there so now that we're on the IO thread we need to properly | 806 // from there so now that we're on the IO thread we need to properly |
675 // initialize the base::WeakPtrFactory. | 807 // initialize the base::WeakPtrFactory. |
676 // TODO(groby): Check if WeakPtrFactory has the same constraint. | 808 // TODO(groby): Check if WeakPtrFactory has the same constraint. |
677 weak_factory_.reset(new base::WeakPtrFactory<Predictor>(this)); | 809 weak_factory_.reset(new base::WeakPtrFactory<Predictor>(this)); |
678 | 810 |
679 // Prefetch these hostnames on startup. | 811 // Prefetch these hostnames on startup. |
680 DnsPrefetchMotivatedList(startup_urls, UrlInfo::STARTUP_LIST_MOTIVATED); | 812 DnsPrefetchMotivatedList(startup_urls, UrlInfo::STARTUP_LIST_MOTIVATED); |
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
837 BrowserThread::PostTask( | 969 BrowserThread::PostTask( |
838 BrowserThread::IO, | 970 BrowserThread::IO, |
839 FROM_HERE, | 971 FROM_HERE, |
840 base::Bind(&Predictor::PreconnectUrlOnIOThread, | 972 base::Bind(&Predictor::PreconnectUrlOnIOThread, |
841 base::Unretained(this), url, first_party_for_cookies, | 973 base::Unretained(this), url, first_party_for_cookies, |
842 motivation, count)); | 974 motivation, count)); |
843 } | 975 } |
844 } | 976 } |
845 | 977 |
846 void Predictor::PreconnectUrlOnIOThread( | 978 void Predictor::PreconnectUrlOnIOThread( |
847 const GURL& url, const GURL& first_party_for_cookies, | 979 const GURL& url, |
848 UrlInfo::ResolutionMotivation motivation, int count) { | 980 const GURL& first_party_for_cookies, |
849 GURL canonical_url(CanonicalizeUrl(url)); | 981 UrlInfo::ResolutionMotivation motivation, |
850 recent_preconnects_.SetRecentlySeen(canonical_url); | 982 int count) { |
| 983 if (motivation == UrlInfo::MOUSE_OVER_MOTIVATED) |
| 984 RecordPreconnectTrigger(url); |
851 | 985 |
852 PreconnectOnIOThread(url, | 986 PreconnectOnIOThread(url, |
853 first_party_for_cookies, | 987 first_party_for_cookies, |
854 motivation, | 988 motivation, |
855 count, | 989 count, |
856 url_request_context_getter_.get()); | 990 url_request_context_getter_.get()); |
857 } | 991 } |
858 | 992 |
859 void Predictor::RecordPreconnectNavigationStats(const GURL& url) { | 993 void Predictor::RecordPreconnectTrigger(const GURL& url) { |
860 UMA_HISTOGRAM_BOOLEAN( | 994 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
861 "Net.PreconnectedNavigation", | 995 if (preconnect_usage_) |
862 recent_preconnects_.WasRecentlySeen(url)); | 996 preconnect_usage_->ObservePreconnect(url); |
| 997 } |
| 998 |
| 999 void Predictor::RecordPreconnectNavigationStat( |
| 1000 const std::vector<GURL>& url_chain, |
| 1001 bool is_subresource) { |
| 1002 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 1003 |
| 1004 if (preconnect_usage_) |
| 1005 preconnect_usage_->ObserveNavigationChain(url_chain, is_subresource); |
| 1006 } |
| 1007 |
| 1008 void Predictor::RecordLinkNavigation(const GURL& url) { |
| 1009 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 1010 if (preconnect_usage_) |
| 1011 preconnect_usage_->ObserveLinkNavigation(url); |
863 } | 1012 } |
864 | 1013 |
865 void Predictor::PredictFrameSubresources(const GURL& url, | 1014 void Predictor::PredictFrameSubresources(const GURL& url, |
866 const GURL& first_party_for_cookies) { | 1015 const GURL& first_party_for_cookies) { |
867 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | 1016 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || |
868 BrowserThread::CurrentlyOn(BrowserThread::IO)); | 1017 BrowserThread::CurrentlyOn(BrowserThread::IO)); |
869 if (!predictor_enabled_) | 1018 if (!predictor_enabled_) |
870 return; | 1019 return; |
871 DCHECK_EQ(url.GetWithEmptyPath(), url); | 1020 DCHECK_EQ(url.GetWithEmptyPath(), url); |
872 // Add one pass through the message loop to allow current navigation to | 1021 // Add one pass through the message loop to allow current navigation to |
(...skipping 354 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1227 IOThread* io_thread, | 1376 IOThread* io_thread, |
1228 net::URLRequestContextGetter* getter) { | 1377 net::URLRequestContextGetter* getter) { |
1229 // Empty function for unittests. | 1378 // Empty function for unittests. |
1230 } | 1379 } |
1231 | 1380 |
1232 void SimplePredictor::ShutdownOnUIThread(PrefService* user_prefs) { | 1381 void SimplePredictor::ShutdownOnUIThread(PrefService* user_prefs) { |
1233 SetShutdown(true); | 1382 SetShutdown(true); |
1234 } | 1383 } |
1235 | 1384 |
1236 } // namespace chrome_browser_net | 1385 } // namespace chrome_browser_net |
OLD | NEW |