| 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/url_info.h" | 5 #include "chrome/browser/net/url_info.h" |
| 6 | 6 |
| 7 #include <ctype.h> | 7 #include <ctype.h> |
| 8 #include <math.h> | 8 #include <math.h> |
| 9 | 9 |
| 10 #include <algorithm> | 10 #include <algorithm> |
| 11 #include <string> | 11 #include <string> |
| 12 | 12 |
| 13 #include "base/format_macros.h" | 13 #include "base/format_macros.h" |
| 14 #include "base/lazy_instance.h" |
| 14 #include "base/logging.h" | 15 #include "base/logging.h" |
| 15 #include "base/metrics/histogram.h" | 16 #include "base/metrics/histogram.h" |
| 16 #include "base/stringprintf.h" | 17 #include "base/stringprintf.h" |
| 17 | 18 |
| 18 using base::Time; | 19 using base::Time; |
| 19 using base::TimeDelta; | 20 using base::TimeDelta; |
| 20 using base::TimeTicks; | 21 using base::TimeTicks; |
| 21 | 22 |
| 22 namespace chrome_browser_net { | 23 namespace chrome_browser_net { |
| 23 | 24 |
| 24 static bool detailed_logging_enabled = false; | 25 namespace { |
| 26 |
| 27 // The number of OS cache entries we can guarantee(?) before cache eviction |
| 28 // might likely take place. |
| 29 const int kMaxGuaranteedDnsCacheSize = 50; |
| 30 |
| 31 // Common low end TTL for sites is 5 minutes. However, DNS servers give us the |
| 32 // remaining time, not the original 5 minutes. Hence it doesn't much matter |
| 33 // whether we found something in the local cache, or an ISP cache, it will on |
| 34 // average be 2.5 minutes before it expires. We could try to model this with |
| 35 // 180 seconds, but simpler is just to do the lookups all the time (wasting OS |
| 36 // calls(?)), and let that OS cache decide what to do (with TTL in hand). We |
| 37 // use a small time to help get some duplicate suppression, in case a page has |
| 38 // a TON of copies of the same domain name, so that we don't thrash the OS to |
| 39 // death. Hopefully it is small enough that we're not hurting our cache hit |
| 40 // rate (i.e., we could always ask the OS). |
| 41 const int kDefaultCacheExpirationDuration = 5; |
| 42 |
| 43 TimeDelta MaxNonNetworkDnsLookupDuration() { |
| 44 return TimeDelta::FromMilliseconds(15); |
| 45 } |
| 46 |
| 47 bool detailed_logging_enabled = false; |
| 48 |
| 49 struct GlobalState { |
| 50 GlobalState() { |
| 51 cache_expiration_duration = |
| 52 TimeDelta::FromSeconds(kDefaultCacheExpirationDuration); |
| 53 } |
| 54 TimeDelta cache_expiration_duration; |
| 55 }; |
| 56 |
| 57 base::LazyInstance<GlobalState>::Leaky global_state; |
| 58 |
| 59 } // anonymous namespace |
| 25 | 60 |
| 26 // Use command line switch to enable detailed logging. | 61 // Use command line switch to enable detailed logging. |
| 27 void EnablePredictorDetailedLog(bool enable) { | 62 void EnablePredictorDetailedLog(bool enable) { |
| 28 detailed_logging_enabled = enable; | 63 detailed_logging_enabled = enable; |
| 29 } | 64 } |
| 30 | 65 |
| 31 // static | 66 // static |
| 32 int UrlInfo::sequence_counter = 1; | 67 int UrlInfo::sequence_counter = 1; |
| 33 | 68 |
| 34 UrlInfo::UrlInfo() | 69 UrlInfo::UrlInfo() |
| 35 : state_(PENDING), | 70 : state_(PENDING), |
| 36 old_prequeue_state_(state_), | 71 old_prequeue_state_(state_), |
| 37 resolve_duration_(kNullDuration), | 72 resolve_duration_(NullDuration()), |
| 38 queue_duration_(kNullDuration), | 73 queue_duration_(NullDuration()), |
| 39 sequence_number_(0), | 74 sequence_number_(0), |
| 40 motivation_(NO_PREFETCH_MOTIVATION), | 75 motivation_(NO_PREFETCH_MOTIVATION), |
| 41 was_linked_(false) { | 76 was_linked_(false) { |
| 42 } | 77 } |
| 43 | 78 |
| 44 UrlInfo::~UrlInfo() {} | 79 UrlInfo::~UrlInfo() {} |
| 45 | 80 |
| 46 bool UrlInfo::NeedsDnsUpdate() { | 81 bool UrlInfo::NeedsDnsUpdate() { |
| 47 switch (state_) { | 82 switch (state_) { |
| 48 case PENDING: // Just now created info. | 83 case PENDING: // Just now created info. |
| 49 return true; | 84 return true; |
| 50 | 85 |
| 51 case QUEUED: // In queue. | 86 case QUEUED: // In queue. |
| 52 case ASSIGNED: // It's being resolved. | 87 case ASSIGNED: // It's being resolved. |
| 53 case ASSIGNED_BUT_MARKED: // It's being resolved. | 88 case ASSIGNED_BUT_MARKED: // It's being resolved. |
| 54 return false; // We're already working on it | 89 return false; // We're already working on it |
| 55 | 90 |
| 56 case NO_SUCH_NAME: // Lookup failed. | 91 case NO_SUCH_NAME: // Lookup failed. |
| 57 case FOUND: // Lookup succeeded. | 92 case FOUND: // Lookup succeeded. |
| 58 return !IsStillCached(); // See if DNS cache expired. | 93 return !IsStillCached(); // See if DNS cache expired. |
| 59 | 94 |
| 60 default: | 95 default: |
| 61 NOTREACHED(); | 96 NOTREACHED(); |
| 62 return false; | 97 return false; |
| 63 } | 98 } |
| 64 } | 99 } |
| 65 | 100 |
| 66 const TimeDelta UrlInfo::kNullDuration(TimeDelta::FromMilliseconds(-1)); | 101 // Used by test ONLY. The value is otherwise constant. |
| 67 | |
| 68 // Common low end TTL for sites is 5 minutes. However, DNS servers give us | |
| 69 // the remaining time, not the original 5 minutes. Hence it doesn't much matter | |
| 70 // whether we found something in the local cache, or an ISP cache, it will | |
| 71 // on average be 2.5 minutes before it expires. We could try to model this with | |
| 72 // 180 seconds, but simpler is just to do the lookups all the time (wasting | |
| 73 // OS calls(?)), and let that OS cache decide what to do (with TTL in hand). | |
| 74 // We use a small time to help get some duplicate suppression, in case a page | |
| 75 // has a TON of copies of the same domain name, so that we don't thrash the OS | |
| 76 // to death. Hopefully it is small enough that we're not hurting our cache hit | |
| 77 // rate (i.e., we could always ask the OS). | |
| 78 TimeDelta UrlInfo::cache_expiration_duration_(TimeDelta::FromSeconds(5)); | |
| 79 | |
| 80 const TimeDelta UrlInfo::kMaxNonNetworkDnsLookupDuration( | |
| 81 TimeDelta::FromMilliseconds(15)); | |
| 82 | |
| 83 // Used by test ONLY. The value is otherwise constant. | |
| 84 // static | 102 // static |
| 85 void UrlInfo::set_cache_expiration(TimeDelta time) { | 103 void UrlInfo::set_cache_expiration(TimeDelta time) { |
| 86 cache_expiration_duration_ = time; | 104 global_state.Pointer()->cache_expiration_duration = time; |
| 87 } | 105 } |
| 88 | 106 |
| 89 // static | 107 // static |
| 90 TimeDelta UrlInfo::get_cache_expiration() { | 108 TimeDelta UrlInfo::get_cache_expiration() { |
| 91 return cache_expiration_duration_; | 109 return global_state.Get().cache_expiration_duration; |
| 92 } | 110 } |
| 93 | 111 |
| 94 void UrlInfo::SetQueuedState(ResolutionMotivation motivation) { | 112 void UrlInfo::SetQueuedState(ResolutionMotivation motivation) { |
| 95 DCHECK(PENDING == state_ || FOUND == state_ || NO_SUCH_NAME == state_); | 113 DCHECK(PENDING == state_ || FOUND == state_ || NO_SUCH_NAME == state_); |
| 96 old_prequeue_state_ = state_; | 114 old_prequeue_state_ = state_; |
| 97 state_ = QUEUED; | 115 state_ = QUEUED; |
| 98 queue_duration_ = resolve_duration_ = kNullDuration; | 116 queue_duration_ = resolve_duration_ = NullDuration(); |
| 99 SetMotivation(motivation); | 117 SetMotivation(motivation); |
| 100 GetDuration(); // Set time_ | 118 GetDuration(); // Set time_ |
| 101 DLogResultsStats("DNS Prefetch in queue"); | 119 DLogResultsStats("DNS Prefetch in queue"); |
| 102 } | 120 } |
| 103 | 121 |
| 104 void UrlInfo::SetAssignedState() { | 122 void UrlInfo::SetAssignedState() { |
| 105 DCHECK(QUEUED == state_); | 123 DCHECK(QUEUED == state_); |
| 106 state_ = ASSIGNED; | 124 state_ = ASSIGNED; |
| 107 queue_duration_ = GetDuration(); | 125 queue_duration_ = GetDuration(); |
| 108 DLogResultsStats("DNS Prefetch assigned"); | 126 DLogResultsStats("DNS Prefetch assigned"); |
| 109 UMA_HISTOGRAM_TIMES("DNS.PrefetchQueue", queue_duration_); | 127 UMA_HISTOGRAM_TIMES("DNS.PrefetchQueue", queue_duration_); |
| 110 } | 128 } |
| 111 | 129 |
| 112 void UrlInfo::RemoveFromQueue() { | 130 void UrlInfo::RemoveFromQueue() { |
| 113 DCHECK(ASSIGNED == state_); | 131 DCHECK(ASSIGNED == state_); |
| 114 state_ = old_prequeue_state_; | 132 state_ = old_prequeue_state_; |
| 115 DLogResultsStats("DNS Prefetch reset to prequeue"); | 133 DLogResultsStats("DNS Prefetch reset to prequeue"); |
| 116 static const TimeDelta kBoundary = TimeDelta::FromSeconds(2); | 134 const TimeDelta kBoundary = TimeDelta::FromSeconds(2); |
| 117 if (queue_duration_ > kBoundary) { | 135 if (queue_duration_ > kBoundary) { |
| 118 UMA_HISTOGRAM_MEDIUM_TIMES("DNS.QueueRecycledDeltaOver2", | 136 UMA_HISTOGRAM_MEDIUM_TIMES("DNS.QueueRecycledDeltaOver2", |
| 119 queue_duration_ - kBoundary); | 137 queue_duration_ - kBoundary); |
| 120 return; | 138 return; |
| 121 } | 139 } |
| 122 // Make a custom linear histogram for the region from 0 to boundary. | 140 // Make a custom linear histogram for the region from 0 to boundary. |
| 123 const size_t kBucketCount = 52; | 141 static const size_t kBucketCount = 52; |
| 124 static base::Histogram* histogram(NULL); | 142 static base::Histogram* histogram(NULL); |
| 125 if (!histogram) | 143 if (!histogram) |
| 126 histogram = base::LinearHistogram::FactoryTimeGet( | 144 histogram = base::LinearHistogram::FactoryTimeGet( |
| 127 "DNS.QueueRecycledUnder2", TimeDelta(), kBoundary, kBucketCount, | 145 "DNS.QueueRecycledUnder2", TimeDelta(), kBoundary, kBucketCount, |
| 128 base::Histogram::kUmaTargetedHistogramFlag); | 146 base::Histogram::kUmaTargetedHistogramFlag); |
| 129 histogram->AddTime(queue_duration_); | 147 histogram->AddTime(queue_duration_); |
| 130 } | 148 } |
| 131 | 149 |
| 132 void UrlInfo::SetPendingDeleteState() { | 150 void UrlInfo::SetPendingDeleteState() { |
| 133 DCHECK(ASSIGNED == state_ || ASSIGNED_BUT_MARKED == state_); | 151 DCHECK(ASSIGNED == state_ || ASSIGNED_BUT_MARKED == state_); |
| 134 state_ = ASSIGNED_BUT_MARKED; | 152 state_ = ASSIGNED_BUT_MARKED; |
| 135 } | 153 } |
| 136 | 154 |
| 137 void UrlInfo::SetFoundState() { | 155 void UrlInfo::SetFoundState() { |
| 138 DCHECK(ASSIGNED == state_); | 156 DCHECK(ASSIGNED == state_); |
| 139 state_ = FOUND; | 157 state_ = FOUND; |
| 140 resolve_duration_ = GetDuration(); | 158 resolve_duration_ = GetDuration(); |
| 141 if (kMaxNonNetworkDnsLookupDuration <= resolve_duration_) { | 159 const TimeDelta max_duration = MaxNonNetworkDnsLookupDuration(); |
| 160 if (max_duration <= resolve_duration_) { |
| 142 UMA_HISTOGRAM_CUSTOM_TIMES("DNS.PrefetchResolution", resolve_duration_, | 161 UMA_HISTOGRAM_CUSTOM_TIMES("DNS.PrefetchResolution", resolve_duration_, |
| 143 kMaxNonNetworkDnsLookupDuration, TimeDelta::FromMinutes(15), 100); | 162 max_duration, TimeDelta::FromMinutes(15), 100); |
| 144 } | 163 } |
| 145 sequence_number_ = sequence_counter++; | 164 sequence_number_ = sequence_counter++; |
| 146 DLogResultsStats("DNS PrefetchFound"); | 165 DLogResultsStats("DNS PrefetchFound"); |
| 147 } | 166 } |
| 148 | 167 |
| 149 void UrlInfo::SetNoSuchNameState() { | 168 void UrlInfo::SetNoSuchNameState() { |
| 150 DCHECK(ASSIGNED == state_); | 169 DCHECK(ASSIGNED == state_); |
| 151 state_ = NO_SUCH_NAME; | 170 state_ = NO_SUCH_NAME; |
| 152 resolve_duration_ = GetDuration(); | 171 resolve_duration_ = GetDuration(); |
| 153 if (kMaxNonNetworkDnsLookupDuration <= resolve_duration_) { | 172 if (MaxNonNetworkDnsLookupDuration() <= resolve_duration_) { |
| 154 DHISTOGRAM_TIMES("DNS.PrefetchNotFoundName", resolve_duration_); | 173 DHISTOGRAM_TIMES("DNS.PrefetchNotFoundName", resolve_duration_); |
| 155 } | 174 } |
| 156 sequence_number_ = sequence_counter++; | 175 sequence_number_ = sequence_counter++; |
| 157 DLogResultsStats("DNS PrefetchNotFound"); | 176 DLogResultsStats("DNS PrefetchNotFound"); |
| 158 } | 177 } |
| 159 | 178 |
| 160 void UrlInfo::SetUrl(const GURL& url) { | 179 void UrlInfo::SetUrl(const GURL& url) { |
| 161 if (url_.is_empty()) // Not yet initialized. | 180 if (url_.is_empty()) // Not yet initialized. |
| 162 url_ = url; | 181 url_ = url; |
| 163 else | 182 else |
| 164 DCHECK_EQ(url_, url); | 183 DCHECK_EQ(url_, url); |
| 165 } | 184 } |
| 166 | 185 |
| 167 // IsStillCached() guesses if the DNS cache still has IP data, | 186 // IsStillCached() guesses if the DNS cache still has IP data, |
| 168 // or at least remembers results about "not finding host." | 187 // or at least remembers results about "not finding host." |
| 169 bool UrlInfo::IsStillCached() const { | 188 bool UrlInfo::IsStillCached() const { |
| 170 DCHECK(FOUND == state_ || NO_SUCH_NAME == state_); | 189 DCHECK(FOUND == state_ || NO_SUCH_NAME == state_); |
| 171 | 190 |
| 172 // Default MS OS does not cache failures. Hence we could return false almost | 191 // Default MS OS does not cache failures. Hence we could return false almost |
| 173 // all the time for that case. However, we'd never try again to prefetch | 192 // all the time for that case. However, we'd never try again to prefetch |
| 174 // the value if we returned false that way. Hence we'll just let the lookup | 193 // the value if we returned false that way. Hence we'll just let the lookup |
| 175 // time out the same way as FOUND case. | 194 // time out the same way as FOUND case. |
| 176 | 195 |
| 177 if (sequence_counter - sequence_number_ > kMaxGuaranteedDnsCacheSize) | 196 if (sequence_counter - sequence_number_ > kMaxGuaranteedDnsCacheSize) |
| 178 return false; | 197 return false; |
| 179 | 198 |
| 180 TimeDelta time_since_resolution = TimeTicks::Now() - time_; | 199 TimeDelta time_since_resolution = TimeTicks::Now() - time_; |
| 181 | 200 return time_since_resolution < global_state.Get().cache_expiration_duration; |
| 182 return time_since_resolution < cache_expiration_duration_; | |
| 183 } | 201 } |
| 184 | 202 |
| 185 void UrlInfo::DLogResultsStats(const char* message) const { | 203 void UrlInfo::DLogResultsStats(const char* message) const { |
| 186 if (!detailed_logging_enabled) | 204 if (!detailed_logging_enabled) |
| 187 return; | 205 return; |
| 188 DVLOG(1) << "\t" << message << "\tq=" << queue_duration().InMilliseconds() | 206 DVLOG(1) << "\t" << message << "\tq=" << queue_duration().InMilliseconds() |
| 189 << "ms,\tr=" << resolve_duration().InMilliseconds() | 207 << "ms,\tr=" << resolve_duration().InMilliseconds() |
| 190 << "ms,\tp=" << sequence_number_ << "\t" << url_.spec(); | 208 << "ms,\tp=" << sequence_number_ << "\t" << url_.spec(); |
| 191 } | 209 } |
| 192 | 210 |
| (...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 341 | 359 |
| 342 case LEARNED_REFERAL_MOTIVATED: | 360 case LEARNED_REFERAL_MOTIVATED: |
| 343 return RemoveJs(referring_url_.spec()); | 361 return RemoveJs(referring_url_.spec()); |
| 344 | 362 |
| 345 default: | 363 default: |
| 346 return ""; | 364 return ""; |
| 347 } | 365 } |
| 348 } | 366 } |
| 349 | 367 |
| 350 } // namespace chrome_browser_net | 368 } // namespace chrome_browser_net |
| OLD | NEW |