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.h" | 25 #include "base/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 two LRU caches that remembers all preconnect and |
| 133 // user link navigations within the 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 the url. |
| 143 void ObservePreconnect(const GURL& url); |
| 144 |
| 145 // Record a user link navigation to the url. |
| 146 void ObserveNavigation(const GURL& url, const GURL& original_url); |
| 147 |
| 148 private: |
| 149 // This tracks whether a preconnect was used in some navigation or not |
| 150 class PreconnectPrecisionStat { |
| 151 public: |
| 152 PreconnectPrecisionStat() |
| 153 : timestamp_(base::TimeTicks::Now()), |
| 154 was_used_(false) { |
| 155 } |
| 156 |
| 157 const base::TimeTicks& timestamp() { return timestamp_; } |
| 158 |
| 159 void set_was_used() { was_used_ = true; } |
| 160 bool was_used() const { return was_used_; } |
| 161 |
| 162 private: |
| 163 base::TimeTicks timestamp_; |
| 164 bool was_used_; |
| 165 }; |
| 166 |
| 167 typedef base::MRUCache<GURL, PreconnectPrecisionStat> MRUPreconnects; |
| 168 MRUPreconnects mru_preconnects_; |
| 169 |
| 170 // This tracks whether a navigation used a preconnected session or not |
| 171 class PreconnectRecallStat { |
| 172 public: |
| 173 PreconnectRecallStat() |
| 174 : timestamp_(base::TimeTicks::Now()), |
| 175 did_use_preconnect_(false) { |
| 176 } |
| 177 |
| 178 const base::TimeTicks& timestamp() { return timestamp_; } |
| 179 |
| 180 void set_did_use_preconnect() { did_use_preconnect_ = true; } |
| 181 bool did_use_preconnect() const { return did_use_preconnect_; } |
| 182 |
| 183 private: |
| 184 base::TimeTicks timestamp_; |
| 185 bool did_use_preconnect_; |
| 186 }; |
| 187 |
| 188 typedef base::MRUCache<GURL, PreconnectRecallStat> MRUNavigations; |
| 189 MRUNavigations mru_navigations_; |
| 190 |
| 191 // The longest time an entry can persist in mru_preconnect_/mru_navigations_ |
| 192 const base::TimeDelta max_duration_; |
| 193 |
| 194 DISALLOW_COPY_AND_ASSIGN(PreconnectUsage); |
| 195 }; |
| 196 |
| 197 Predictor::PreconnectUsage::PreconnectUsage() |
| 198 : mru_preconnects_(MRUPreconnects::NO_AUTO_EVICT), |
| 199 mru_navigations_(MRUNavigations::NO_AUTO_EVICT), |
| 200 max_duration_(base::TimeDelta::FromSeconds( |
| 201 Predictor::kMaxUnusedSocketLifetimeSecondsWithoutAGet)) { |
| 202 } |
| 203 |
| 204 Predictor::PreconnectUsage::~PreconnectUsage() {} |
| 205 |
| 206 void Predictor::PreconnectUsage::ObservePreconnect(const GURL& url) { |
| 207 GURL canonical_url(Predictor::CanonicalizeUrl(url)); |
| 208 mru_preconnects_.Put(canonical_url, PreconnectPrecisionStat()); |
| 209 } |
| 210 |
| 211 void Predictor::PreconnectUsage::ObserveNavigation(const GURL& url, |
| 212 const GURL& original_url) { |
| 213 GURL canonical_url(Predictor::CanonicalizeUrl(url)); |
| 214 |
| 215 // Evict any overly old entries and record stats |
| 216 base::TimeTicks now = base::TimeTicks::Now(); |
| 217 |
| 218 MRUPreconnects::reverse_iterator eldestPreconnect = mru_preconnects_.rbegin(); |
| 219 while (!mru_preconnects_.empty()) { |
| 220 DCHECK(eldestPreconnect == mru_preconnects_.rbegin()); |
| 221 if (now - eldestPreconnect->second.timestamp() < max_duration_) |
| 222 break; |
| 223 |
| 224 UMA_HISTOGRAM_BOOLEAN("Net.PreconnectTriggerUsed", |
| 225 eldestPreconnect->second.was_used()); |
| 226 eldestPreconnect = mru_preconnects_.Erase(eldestPreconnect); |
| 227 } |
| 228 |
| 229 MRUNavigations::reverse_iterator eldestNavigation = mru_navigations_.rbegin(); |
| 230 while (!mru_navigations_.empty()) { |
| 231 DCHECK(eldestNavigation == mru_navigations_.rbegin()); |
| 232 if (now - eldestNavigation->second.timestamp() < max_duration_) |
| 233 break; |
| 234 |
| 235 UMA_HISTOGRAM_BOOLEAN("Net.PreconnectedNavigation", |
| 236 eldestNavigation->second.did_use_preconnect()); |
| 237 eldestNavigation = mru_navigations_.Erase(eldestNavigation); |
| 238 } |
| 239 |
| 240 // Record the preconnect trigger for the url as used if exist |
| 241 MRUNavigations::iterator itNavigation = mru_navigations_.Peek(original_url); |
| 242 if (itNavigation == mru_navigations_.end()) |
| 243 itNavigation = mru_navigations_.Put(original_url, PreconnectRecallStat()); |
| 244 |
| 245 MRUPreconnects::iterator itPreconnect = mru_preconnects_.Peek(canonical_url); |
| 246 bool wasPreconnected = (itPreconnect != mru_preconnects_.end()); |
| 247 if (wasPreconnected) { |
| 248 itPreconnect->second.set_was_used(); |
| 249 itNavigation->second.set_did_use_preconnect(); |
| 250 } |
| 251 } |
| 252 |
128 Predictor::Predictor(bool preconnect_enabled) | 253 Predictor::Predictor(bool preconnect_enabled) |
129 : url_request_context_getter_(NULL), | 254 : url_request_context_getter_(NULL), |
130 predictor_enabled_(true), | 255 predictor_enabled_(true), |
131 peak_pending_lookups_(0), | 256 peak_pending_lookups_(0), |
132 shutdown_(false), | 257 shutdown_(false), |
133 max_concurrent_dns_lookups_(g_max_parallel_resolves), | 258 max_concurrent_dns_lookups_(g_max_parallel_resolves), |
134 max_dns_queue_delay_( | 259 max_dns_queue_delay_( |
135 TimeDelta::FromMilliseconds(g_max_queueing_delay_ms)), | 260 TimeDelta::FromMilliseconds(g_max_queueing_delay_ms)), |
136 host_resolver_(NULL), | 261 host_resolver_(NULL), |
137 preconnect_enabled_(preconnect_enabled), | 262 preconnect_enabled_(preconnect_enabled), |
138 consecutive_omnibox_preconnect_count_(0), | 263 consecutive_omnibox_preconnect_count_(0), |
139 recent_preconnects_( | |
140 TimeDelta::FromSeconds(kMaxUnusedSocketLifetimeSecondsWithoutAGet)), | |
141 next_trim_time_(base::TimeTicks::Now() + | 264 next_trim_time_(base::TimeTicks::Now() + |
142 TimeDelta::FromHours(kDurationBetweenTrimmingsHours)) { | 265 TimeDelta::FromHours(kDurationBetweenTrimmingsHours)) { |
143 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 266 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
144 } | 267 } |
145 | 268 |
146 Predictor::~Predictor() { | 269 Predictor::~Predictor() { |
147 // TODO(rlp): Add DCHECK for CurrentlyOn(BrowserThread::IO) when the | 270 // TODO(rlp): Add DCHECK for CurrentlyOn(BrowserThread::IO) when the |
148 // ProfileManagerTest has been updated with a mock profile. | 271 // ProfileManagerTest has been updated with a mock profile. |
149 DCHECK(shutdown_); | 272 DCHECK(shutdown_); |
150 } | 273 } |
(...skipping 509 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
660 void Predictor::FinalizeInitializationOnIOThread( | 783 void Predictor::FinalizeInitializationOnIOThread( |
661 const UrlList& startup_urls, | 784 const UrlList& startup_urls, |
662 base::ListValue* referral_list, | 785 base::ListValue* referral_list, |
663 IOThread* io_thread, | 786 IOThread* io_thread, |
664 bool predictor_enabled) { | 787 bool predictor_enabled) { |
665 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 788 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
666 | 789 |
667 predictor_enabled_ = predictor_enabled; | 790 predictor_enabled_ = predictor_enabled; |
668 initial_observer_.reset(new InitialObserver()); | 791 initial_observer_.reset(new InitialObserver()); |
669 host_resolver_ = io_thread->globals()->host_resolver.get(); | 792 host_resolver_ = io_thread->globals()->host_resolver.get(); |
| 793 preconnect_usage_.reset(new PreconnectUsage()); |
670 | 794 |
671 // base::WeakPtrFactory instances need to be created and destroyed | 795 // base::WeakPtrFactory instances need to be created and destroyed |
672 // on the same thread. The predictor lives on the IO thread and will die | 796 // on the same thread. The predictor lives on the IO thread and will die |
673 // from there so now that we're on the IO thread we need to properly | 797 // from there so now that we're on the IO thread we need to properly |
674 // initialize the base::WeakPtrFactory. | 798 // initialize the base::WeakPtrFactory. |
675 // TODO(groby): Check if WeakPtrFactory has the same constraint. | 799 // TODO(groby): Check if WeakPtrFactory has the same constraint. |
676 weak_factory_.reset(new base::WeakPtrFactory<Predictor>(this)); | 800 weak_factory_.reset(new base::WeakPtrFactory<Predictor>(this)); |
677 | 801 |
678 // Prefetch these hostnames on startup. | 802 // Prefetch these hostnames on startup. |
679 DnsPrefetchMotivatedList(startup_urls, UrlInfo::STARTUP_LIST_MOTIVATED); | 803 DnsPrefetchMotivatedList(startup_urls, UrlInfo::STARTUP_LIST_MOTIVATED); |
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
838 FROM_HERE, | 962 FROM_HERE, |
839 base::Bind(&Predictor::PreconnectUrlOnIOThread, | 963 base::Bind(&Predictor::PreconnectUrlOnIOThread, |
840 base::Unretained(this), url, first_party_for_cookies, | 964 base::Unretained(this), url, first_party_for_cookies, |
841 motivation, count)); | 965 motivation, count)); |
842 } | 966 } |
843 } | 967 } |
844 | 968 |
845 void Predictor::PreconnectUrlOnIOThread( | 969 void Predictor::PreconnectUrlOnIOThread( |
846 const GURL& url, const GURL& first_party_for_cookies, | 970 const GURL& url, const GURL& first_party_for_cookies, |
847 UrlInfo::ResolutionMotivation motivation, int count) { | 971 UrlInfo::ResolutionMotivation motivation, int count) { |
848 GURL canonical_url(CanonicalizeUrl(url)); | |
849 recent_preconnects_.SetRecentlySeen(canonical_url); | |
850 | |
851 PreconnectOnIOThread(url, | 972 PreconnectOnIOThread(url, |
852 first_party_for_cookies, | 973 first_party_for_cookies, |
853 motivation, | 974 motivation, |
854 count, | 975 count, |
855 url_request_context_getter_.get()); | 976 url_request_context_getter_.get()); |
856 } | 977 } |
857 | 978 |
858 void Predictor::RecordPreconnectNavigationStats(const GURL& url) { | 979 void Predictor::RecordPreconnectTrigger(const GURL& url) { |
859 UMA_HISTOGRAM_BOOLEAN( | 980 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || |
860 "Net.PreconnectedNavigation", | 981 BrowserThread::CurrentlyOn(BrowserThread::IO)); |
861 recent_preconnects_.WasRecentlySeen(url)); | 982 |
| 983 if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { |
| 984 RecordPreconnectTriggerOnIOThread(url); |
| 985 } else { |
| 986 BrowserThread::PostTask( |
| 987 BrowserThread::IO, |
| 988 FROM_HERE, |
| 989 base::Bind(&Predictor::RecordPreconnectTriggerOnIOThread, |
| 990 base::Unretained(this), url)); |
| 991 } |
| 992 } |
| 993 |
| 994 void Predictor::RecordPreconnectTriggerOnIOThread(const GURL& url) { |
| 995 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 996 preconnect_usage_->ObservePreconnect(url); |
| 997 } |
| 998 |
| 999 void Predictor::RecordPreconnectNavigationStat(const GURL& url, |
| 1000 const GURL& original_url) { |
| 1001 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || |
| 1002 BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 1003 |
| 1004 if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { |
| 1005 RecordPreconnectNavigationStatOnIOThread(url, original_url); |
| 1006 } else { |
| 1007 BrowserThread::PostTask( |
| 1008 BrowserThread::IO, |
| 1009 FROM_HERE, |
| 1010 base::Bind(&Predictor::RecordPreconnectNavigationStatOnIOThread, |
| 1011 base::Unretained(this), url, original_url)); |
| 1012 } |
| 1013 } |
| 1014 |
| 1015 void Predictor::RecordPreconnectNavigationStatOnIOThread( |
| 1016 const GURL& url, |
| 1017 const GURL& original_url) { |
| 1018 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 1019 preconnect_usage_->ObserveNavigation(url, original_url); |
862 } | 1020 } |
863 | 1021 |
864 void Predictor::PredictFrameSubresources(const GURL& url, | 1022 void Predictor::PredictFrameSubresources(const GURL& url, |
865 const GURL& first_party_for_cookies) { | 1023 const GURL& first_party_for_cookies) { |
866 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | 1024 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || |
867 BrowserThread::CurrentlyOn(BrowserThread::IO)); | 1025 BrowserThread::CurrentlyOn(BrowserThread::IO)); |
868 if (!predictor_enabled_) | 1026 if (!predictor_enabled_) |
869 return; | 1027 return; |
870 DCHECK_EQ(url.GetWithEmptyPath(), url); | 1028 DCHECK_EQ(url.GetWithEmptyPath(), url); |
871 // Add one pass through the message loop to allow current navigation to | 1029 // 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... |
1226 IOThread* io_thread, | 1384 IOThread* io_thread, |
1227 net::URLRequestContextGetter* getter) { | 1385 net::URLRequestContextGetter* getter) { |
1228 // Empty function for unittests. | 1386 // Empty function for unittests. |
1229 } | 1387 } |
1230 | 1388 |
1231 void SimplePredictor::ShutdownOnUIThread(PrefService* user_prefs) { | 1389 void SimplePredictor::ShutdownOnUIThread(PrefService* user_prefs) { |
1232 SetShutdown(true); | 1390 SetShutdown(true); |
1233 } | 1391 } |
1234 | 1392 |
1235 } // namespace chrome_browser_net | 1393 } // namespace chrome_browser_net |
OLD | NEW |