Chromium Code Reviews| 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 | |
|
jar (doing other things)
2013/08/01 22:56:47
FWIW: The long standing bug in this algorithm is t
kouhei (in TOK)
2013/08/02 06:18:03
My understanding is that per-socket utilization co
| |
| 131 // gather precision/recall for user-event based preconnect triggers. | |
| 132 // Stats are gathered via a LRU cach that remembers all preconnect within the | |
|
jar (doing other things)
2013/08/01 22:56:47
nit: cache-->cache
kouhei (in TOK)
2013/08/02 06:18:03
Done.
| |
| 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 | |
|
jar (doing other things)
2013/08/01 22:56:47
Per comment: We count access (still), and not simu
| |
| 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 | |
|
jar (doing other things)
2013/08/01 22:56:47
nit: Period at end of sentences.
kouhei (in TOK)
2013/08/02 06:18:03
Done.
| |
| 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 | |
|
jar (doing other things)
2013/08/01 22:56:47
nit: period
kouhei (in TOK)
2013/08/02 06:18:03
Done.
| |
| 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, bool is_subresource) { | |
|
jar (doing other things)
2013/08/01 22:56:47
nit: one arg per line.
kouhei (in TOK)
2013/08/02 06:18:03
Done.
| |
| 215 if (url_chain.empty()) | |
| 216 return; | |
| 217 | |
| 218 if (!is_subresource) | |
| 219 recent_navigation_chain_ = url_chain; | |
| 220 | |
| 221 GURL canonical_url(Predictor::CanonicalizeUrl(url_chain.back())); | |
| 222 | |
| 223 // Record the preconnect trigger for the url as used if exist | |
| 224 MRUPreconnects::iterator itPreconnect = mru_preconnects_.Peek(canonical_url); | |
| 225 bool was_preconnected = (itPreconnect != mru_preconnects_.end()); | |
| 226 if (was_preconnected) | |
| 227 itPreconnect->second.set_was_used(); | |
| 228 | |
| 229 // This is an UMA which was named incorrectly. This actually measures the | |
| 230 // ratio of URLRequests which have used a preconnected session. | |
| 231 UMA_HISTOGRAM_BOOLEAN("Net.PreconnectedNavigation", was_preconnected); | |
| 232 } | |
| 233 | |
| 234 void Predictor::PreconnectUsage::ObserveLinkNavigation(const GURL& url) { | |
| 235 if (recent_navigation_chain_.empty() || | |
| 236 url != recent_navigation_chain_.back()) { | |
| 237 // The navigation chain is not available for this navigation. | |
| 238 recent_navigation_chain_.clear(); | |
| 239 recent_navigation_chain_.push_back(url); | |
| 240 } | |
| 241 | |
| 242 // See if the link navigation involved preconnected session. | |
| 243 bool did_use_preconnect = false; | |
| 244 for (std::vector<GURL>::const_iterator it = recent_navigation_chain_.begin(); | |
| 245 it != recent_navigation_chain_.end(); | |
| 246 ++it) { | |
| 247 GURL canonical_url(Predictor::CanonicalizeUrl(*it)); | |
| 248 | |
| 249 // Record the preconnect trigger for the url as used if exist | |
| 250 MRUPreconnects::iterator itPreconnect = | |
| 251 mru_preconnects_.Peek(canonical_url); | |
| 252 bool was_preconnected = (itPreconnect != mru_preconnects_.end()); | |
| 253 if (was_preconnected) | |
| 254 did_use_preconnect = true; | |
| 255 } | |
| 256 | |
| 257 UMA_HISTOGRAM_BOOLEAN("Net.PreconnectedLinkNavigations", did_use_preconnect); | |
| 258 } | |
| 259 | |
| 128 Predictor::Predictor(bool preconnect_enabled) | 260 Predictor::Predictor(bool preconnect_enabled) |
| 129 : url_request_context_getter_(NULL), | 261 : url_request_context_getter_(NULL), |
| 130 predictor_enabled_(true), | 262 predictor_enabled_(true), |
| 131 peak_pending_lookups_(0), | 263 peak_pending_lookups_(0), |
| 132 shutdown_(false), | 264 shutdown_(false), |
| 133 max_concurrent_dns_lookups_(g_max_parallel_resolves), | 265 max_concurrent_dns_lookups_(g_max_parallel_resolves), |
| 134 max_dns_queue_delay_( | 266 max_dns_queue_delay_( |
| 135 TimeDelta::FromMilliseconds(g_max_queueing_delay_ms)), | 267 TimeDelta::FromMilliseconds(g_max_queueing_delay_ms)), |
| 136 host_resolver_(NULL), | 268 host_resolver_(NULL), |
| 137 preconnect_enabled_(preconnect_enabled), | 269 preconnect_enabled_(preconnect_enabled), |
| 138 consecutive_omnibox_preconnect_count_(0), | 270 consecutive_omnibox_preconnect_count_(0), |
| 139 recent_preconnects_( | |
| 140 TimeDelta::FromSeconds(kMaxUnusedSocketLifetimeSecondsWithoutAGet)), | |
| 141 next_trim_time_(base::TimeTicks::Now() + | 271 next_trim_time_(base::TimeTicks::Now() + |
| 142 TimeDelta::FromHours(kDurationBetweenTrimmingsHours)) { | 272 TimeDelta::FromHours(kDurationBetweenTrimmingsHours)) { |
| 143 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 273 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 144 } | 274 } |
| 145 | 275 |
| 146 Predictor::~Predictor() { | 276 Predictor::~Predictor() { |
| 147 // TODO(rlp): Add DCHECK for CurrentlyOn(BrowserThread::IO) when the | 277 // TODO(rlp): Add DCHECK for CurrentlyOn(BrowserThread::IO) when the |
| 148 // ProfileManagerTest has been updated with a mock profile. | 278 // ProfileManagerTest has been updated with a mock profile. |
| 149 DCHECK(shutdown_); | 279 DCHECK(shutdown_); |
| 150 } | 280 } |
| (...skipping 510 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 661 void Predictor::FinalizeInitializationOnIOThread( | 791 void Predictor::FinalizeInitializationOnIOThread( |
| 662 const UrlList& startup_urls, | 792 const UrlList& startup_urls, |
| 663 base::ListValue* referral_list, | 793 base::ListValue* referral_list, |
| 664 IOThread* io_thread, | 794 IOThread* io_thread, |
| 665 bool predictor_enabled) { | 795 bool predictor_enabled) { |
| 666 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 796 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 667 | 797 |
| 668 predictor_enabled_ = predictor_enabled; | 798 predictor_enabled_ = predictor_enabled; |
| 669 initial_observer_.reset(new InitialObserver()); | 799 initial_observer_.reset(new InitialObserver()); |
| 670 host_resolver_ = io_thread->globals()->host_resolver.get(); | 800 host_resolver_ = io_thread->globals()->host_resolver.get(); |
| 801 preconnect_usage_.reset(new PreconnectUsage()); | |
| 671 | 802 |
| 672 // base::WeakPtrFactory instances need to be created and destroyed | 803 // base::WeakPtrFactory instances need to be created and destroyed |
| 673 // on the same thread. The predictor lives on the IO thread and will die | 804 // 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 | 805 // from there so now that we're on the IO thread we need to properly |
| 675 // initialize the base::WeakPtrFactory. | 806 // initialize the base::WeakPtrFactory. |
| 676 // TODO(groby): Check if WeakPtrFactory has the same constraint. | 807 // TODO(groby): Check if WeakPtrFactory has the same constraint. |
| 677 weak_factory_.reset(new base::WeakPtrFactory<Predictor>(this)); | 808 weak_factory_.reset(new base::WeakPtrFactory<Predictor>(this)); |
| 678 | 809 |
| 679 // Prefetch these hostnames on startup. | 810 // Prefetch these hostnames on startup. |
| 680 DnsPrefetchMotivatedList(startup_urls, UrlInfo::STARTUP_LIST_MOTIVATED); | 811 DnsPrefetchMotivatedList(startup_urls, UrlInfo::STARTUP_LIST_MOTIVATED); |
| (...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 837 BrowserThread::PostTask( | 968 BrowserThread::PostTask( |
| 838 BrowserThread::IO, | 969 BrowserThread::IO, |
| 839 FROM_HERE, | 970 FROM_HERE, |
| 840 base::Bind(&Predictor::PreconnectUrlOnIOThread, | 971 base::Bind(&Predictor::PreconnectUrlOnIOThread, |
| 841 base::Unretained(this), url, first_party_for_cookies, | 972 base::Unretained(this), url, first_party_for_cookies, |
| 842 motivation, count)); | 973 motivation, count)); |
| 843 } | 974 } |
| 844 } | 975 } |
| 845 | 976 |
| 846 void Predictor::PreconnectUrlOnIOThread( | 977 void Predictor::PreconnectUrlOnIOThread( |
| 847 const GURL& url, const GURL& first_party_for_cookies, | 978 const GURL& url, |
| 848 UrlInfo::ResolutionMotivation motivation, int count) { | 979 const GURL& first_party_for_cookies, |
| 849 GURL canonical_url(CanonicalizeUrl(url)); | 980 UrlInfo::ResolutionMotivation motivation, |
| 850 recent_preconnects_.SetRecentlySeen(canonical_url); | 981 int count) { |
| 851 | |
| 852 PreconnectOnIOThread(url, | 982 PreconnectOnIOThread(url, |
| 853 first_party_for_cookies, | 983 first_party_for_cookies, |
| 854 motivation, | 984 motivation, |
| 855 count, | 985 count, |
| 856 url_request_context_getter_.get()); | 986 url_request_context_getter_.get()); |
| 857 } | 987 } |
| 858 | 988 |
| 859 void Predictor::RecordPreconnectNavigationStats(const GURL& url) { | 989 void Predictor::RecordPreconnectTrigger(const GURL& url) { |
| 860 UMA_HISTOGRAM_BOOLEAN( | 990 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || |
| 861 "Net.PreconnectedNavigation", | 991 BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 862 recent_preconnects_.WasRecentlySeen(url)); | 992 |
| 993 if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { | |
| 994 RecordPreconnectTriggerOnIOThread(url); | |
| 995 } else { | |
| 996 BrowserThread::PostTask( | |
| 997 BrowserThread::IO, | |
| 998 FROM_HERE, | |
| 999 base::Bind(&Predictor::RecordPreconnectTriggerOnIOThread, | |
| 1000 base::Unretained(this), url)); | |
| 1001 } | |
| 1002 } | |
| 1003 | |
| 1004 void Predictor::RecordPreconnectTriggerOnIOThread(const GURL& url) { | |
| 1005 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 1006 if (preconnect_usage_) | |
| 1007 preconnect_usage_->ObservePreconnect(url); | |
| 1008 } | |
| 1009 | |
| 1010 void Predictor::RecordPreconnectNavigationStat( | |
| 1011 const std::vector<GURL>& url_chain, bool is_subresource) { | |
|
jar (doing other things)
2013/08/01 22:56:47
nit: one arg per line.
kouhei (in TOK)
2013/08/02 06:18:03
Done.
| |
| 1012 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 1013 | |
| 1014 if (preconnect_usage_) | |
| 1015 preconnect_usage_->ObserveNavigationChain(url_chain, is_subresource); | |
| 1016 } | |
| 1017 | |
| 1018 void Predictor::RecordLinkNavigation(const GURL& url) { | |
| 1019 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | |
| 1020 BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 1021 | |
| 1022 if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { | |
| 1023 RecordLinkNavigationOnIOThread(url); | |
| 1024 } else { | |
| 1025 BrowserThread::PostTask( | |
| 1026 BrowserThread::IO, | |
| 1027 FROM_HERE, | |
| 1028 base::Bind(&Predictor::RecordLinkNavigationOnIOThread, | |
| 1029 base::Unretained(this), url)); | |
| 1030 } | |
| 1031 } | |
| 1032 | |
| 1033 void Predictor::RecordLinkNavigationOnIOThread(const GURL& url) { | |
| 1034 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 1035 if (preconnect_usage_) | |
| 1036 preconnect_usage_->ObserveLinkNavigation(url); | |
| 863 } | 1037 } |
| 864 | 1038 |
| 865 void Predictor::PredictFrameSubresources(const GURL& url, | 1039 void Predictor::PredictFrameSubresources(const GURL& url, |
| 866 const GURL& first_party_for_cookies) { | 1040 const GURL& first_party_for_cookies) { |
| 867 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | 1041 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || |
| 868 BrowserThread::CurrentlyOn(BrowserThread::IO)); | 1042 BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 869 if (!predictor_enabled_) | 1043 if (!predictor_enabled_) |
| 870 return; | 1044 return; |
| 871 DCHECK_EQ(url.GetWithEmptyPath(), url); | 1045 DCHECK_EQ(url.GetWithEmptyPath(), url); |
| 872 // Add one pass through the message loop to allow current navigation to | 1046 // 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, | 1401 IOThread* io_thread, |
| 1228 net::URLRequestContextGetter* getter) { | 1402 net::URLRequestContextGetter* getter) { |
| 1229 // Empty function for unittests. | 1403 // Empty function for unittests. |
| 1230 } | 1404 } |
| 1231 | 1405 |
| 1232 void SimplePredictor::ShutdownOnUIThread(PrefService* user_prefs) { | 1406 void SimplePredictor::ShutdownOnUIThread(PrefService* user_prefs) { |
| 1233 SetShutdown(true); | 1407 SetShutdown(true); |
| 1234 } | 1408 } |
| 1235 | 1409 |
| 1236 } // namespace chrome_browser_net | 1410 } // namespace chrome_browser_net |
| OLD | NEW |