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 TimeDelta cache_expiration_duration; | |
51 }; | |
52 | |
53 struct InitializeGlobalState { | |
54 static const bool kRegisterOnExit = false; | |
55 static const bool kAllowedToAccessOnNonjoinableThread = true; | |
56 | |
57 static GlobalState* New(void* instance) { | |
battre
2012/03/08 18:48:43
Is this necessary? Could you not just use a constr
Philippe
2012/03/09 08:48:09
Indeed. I don't know how I managed to come up with
| |
58 GlobalState* const global = new(instance) GlobalState(); | |
59 global->cache_expiration_duration = | |
60 TimeDelta::FromSeconds(kDefaultCacheExpirationDuration); | |
61 return global; | |
62 } | |
63 | |
64 static void Delete(GlobalState* /* instance */) { /* Leaky */ } | |
65 }; | |
66 | |
67 base::LazyInstance<GlobalState, InitializeGlobalState> global_state; | |
68 | |
69 } // anonymous namespace | |
25 | 70 |
26 // Use command line switch to enable detailed logging. | 71 // Use command line switch to enable detailed logging. |
27 void EnablePredictorDetailedLog(bool enable) { | 72 void EnablePredictorDetailedLog(bool enable) { |
28 detailed_logging_enabled = enable; | 73 detailed_logging_enabled = enable; |
29 } | 74 } |
30 | 75 |
31 // static | 76 // static |
32 int UrlInfo::sequence_counter = 1; | 77 int UrlInfo::sequence_counter = 1; |
33 | 78 |
34 UrlInfo::UrlInfo() | 79 UrlInfo::UrlInfo() |
35 : state_(PENDING), | 80 : state_(PENDING), |
36 old_prequeue_state_(state_), | 81 old_prequeue_state_(state_), |
37 resolve_duration_(kNullDuration), | 82 resolve_duration_(NullDuration()), |
38 queue_duration_(kNullDuration), | 83 queue_duration_(NullDuration()), |
39 sequence_number_(0), | 84 sequence_number_(0), |
40 motivation_(NO_PREFETCH_MOTIVATION), | 85 motivation_(NO_PREFETCH_MOTIVATION), |
41 was_linked_(false) { | 86 was_linked_(false) { |
42 } | 87 } |
43 | 88 |
44 UrlInfo::~UrlInfo() {} | 89 UrlInfo::~UrlInfo() {} |
45 | 90 |
46 bool UrlInfo::NeedsDnsUpdate() { | 91 bool UrlInfo::NeedsDnsUpdate() { |
47 switch (state_) { | 92 switch (state_) { |
48 case PENDING: // Just now created info. | 93 case PENDING: // Just now created info. |
49 return true; | 94 return true; |
50 | 95 |
51 case QUEUED: // In queue. | 96 case QUEUED: // In queue. |
52 case ASSIGNED: // It's being resolved. | 97 case ASSIGNED: // It's being resolved. |
53 case ASSIGNED_BUT_MARKED: // It's being resolved. | 98 case ASSIGNED_BUT_MARKED: // It's being resolved. |
54 return false; // We're already working on it | 99 return false; // We're already working on it |
55 | 100 |
56 case NO_SUCH_NAME: // Lookup failed. | 101 case NO_SUCH_NAME: // Lookup failed. |
57 case FOUND: // Lookup succeeded. | 102 case FOUND: // Lookup succeeded. |
58 return !IsStillCached(); // See if DNS cache expired. | 103 return !IsStillCached(); // See if DNS cache expired. |
59 | 104 |
60 default: | 105 default: |
61 NOTREACHED(); | 106 NOTREACHED(); |
62 return false; | 107 return false; |
63 } | 108 } |
64 } | 109 } |
65 | 110 |
66 const TimeDelta UrlInfo::kNullDuration(TimeDelta::FromMilliseconds(-1)); | 111 // 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 | 112 // static |
85 void UrlInfo::set_cache_expiration(TimeDelta time) { | 113 void UrlInfo::set_cache_expiration(TimeDelta time) { |
86 cache_expiration_duration_ = time; | 114 global_state.Pointer()->cache_expiration_duration = time; |
87 } | 115 } |
88 | 116 |
89 // static | 117 // static |
90 TimeDelta UrlInfo::get_cache_expiration() { | 118 TimeDelta UrlInfo::get_cache_expiration() { |
91 return cache_expiration_duration_; | 119 return global_state.Get().cache_expiration_duration; |
92 } | 120 } |
93 | 121 |
94 void UrlInfo::SetQueuedState(ResolutionMotivation motivation) { | 122 void UrlInfo::SetQueuedState(ResolutionMotivation motivation) { |
95 DCHECK(PENDING == state_ || FOUND == state_ || NO_SUCH_NAME == state_); | 123 DCHECK(PENDING == state_ || FOUND == state_ || NO_SUCH_NAME == state_); |
96 old_prequeue_state_ = state_; | 124 old_prequeue_state_ = state_; |
97 state_ = QUEUED; | 125 state_ = QUEUED; |
98 queue_duration_ = resolve_duration_ = kNullDuration; | 126 queue_duration_ = resolve_duration_ = NullDuration(); |
99 SetMotivation(motivation); | 127 SetMotivation(motivation); |
100 GetDuration(); // Set time_ | 128 GetDuration(); // Set time_ |
101 DLogResultsStats("DNS Prefetch in queue"); | 129 DLogResultsStats("DNS Prefetch in queue"); |
102 } | 130 } |
103 | 131 |
104 void UrlInfo::SetAssignedState() { | 132 void UrlInfo::SetAssignedState() { |
105 DCHECK(QUEUED == state_); | 133 DCHECK(QUEUED == state_); |
106 state_ = ASSIGNED; | 134 state_ = ASSIGNED; |
107 queue_duration_ = GetDuration(); | 135 queue_duration_ = GetDuration(); |
108 DLogResultsStats("DNS Prefetch assigned"); | 136 DLogResultsStats("DNS Prefetch assigned"); |
109 UMA_HISTOGRAM_TIMES("DNS.PrefetchQueue", queue_duration_); | 137 UMA_HISTOGRAM_TIMES("DNS.PrefetchQueue", queue_duration_); |
110 } | 138 } |
111 | 139 |
112 void UrlInfo::RemoveFromQueue() { | 140 void UrlInfo::RemoveFromQueue() { |
113 DCHECK(ASSIGNED == state_); | 141 DCHECK(ASSIGNED == state_); |
114 state_ = old_prequeue_state_; | 142 state_ = old_prequeue_state_; |
115 DLogResultsStats("DNS Prefetch reset to prequeue"); | 143 DLogResultsStats("DNS Prefetch reset to prequeue"); |
116 static const TimeDelta kBoundary = TimeDelta::FromSeconds(2); | 144 const TimeDelta kBoundary = TimeDelta::FromSeconds(2); |
117 if (queue_duration_ > kBoundary) { | 145 if (queue_duration_ > kBoundary) { |
118 UMA_HISTOGRAM_MEDIUM_TIMES("DNS.QueueRecycledDeltaOver2", | 146 UMA_HISTOGRAM_MEDIUM_TIMES("DNS.QueueRecycledDeltaOver2", |
119 queue_duration_ - kBoundary); | 147 queue_duration_ - kBoundary); |
120 return; | 148 return; |
121 } | 149 } |
122 // Make a custom linear histogram for the region from 0 to boundary. | 150 // Make a custom linear histogram for the region from 0 to boundary. |
123 const size_t kBucketCount = 52; | 151 static const size_t kBucketCount = 52; |
124 static base::Histogram* histogram(NULL); | 152 static base::Histogram* histogram(NULL); |
125 if (!histogram) | 153 if (!histogram) |
126 histogram = base::LinearHistogram::FactoryTimeGet( | 154 histogram = base::LinearHistogram::FactoryTimeGet( |
127 "DNS.QueueRecycledUnder2", TimeDelta(), kBoundary, kBucketCount, | 155 "DNS.QueueRecycledUnder2", TimeDelta(), kBoundary, kBucketCount, |
128 base::Histogram::kUmaTargetedHistogramFlag); | 156 base::Histogram::kUmaTargetedHistogramFlag); |
129 histogram->AddTime(queue_duration_); | 157 histogram->AddTime(queue_duration_); |
130 } | 158 } |
131 | 159 |
132 void UrlInfo::SetPendingDeleteState() { | 160 void UrlInfo::SetPendingDeleteState() { |
133 DCHECK(ASSIGNED == state_ || ASSIGNED_BUT_MARKED == state_); | 161 DCHECK(ASSIGNED == state_ || ASSIGNED_BUT_MARKED == state_); |
134 state_ = ASSIGNED_BUT_MARKED; | 162 state_ = ASSIGNED_BUT_MARKED; |
135 } | 163 } |
136 | 164 |
137 void UrlInfo::SetFoundState() { | 165 void UrlInfo::SetFoundState() { |
138 DCHECK(ASSIGNED == state_); | 166 DCHECK(ASSIGNED == state_); |
139 state_ = FOUND; | 167 state_ = FOUND; |
140 resolve_duration_ = GetDuration(); | 168 resolve_duration_ = GetDuration(); |
141 if (kMaxNonNetworkDnsLookupDuration <= resolve_duration_) { | 169 const TimeDelta max_duration = MaxNonNetworkDnsLookupDuration(); |
170 if (max_duration <= resolve_duration_) { | |
142 UMA_HISTOGRAM_CUSTOM_TIMES("DNS.PrefetchResolution", resolve_duration_, | 171 UMA_HISTOGRAM_CUSTOM_TIMES("DNS.PrefetchResolution", resolve_duration_, |
143 kMaxNonNetworkDnsLookupDuration, TimeDelta::FromMinutes(15), 100); | 172 max_duration, TimeDelta::FromMinutes(15), 100); |
144 } | 173 } |
145 sequence_number_ = sequence_counter++; | 174 sequence_number_ = sequence_counter++; |
146 DLogResultsStats("DNS PrefetchFound"); | 175 DLogResultsStats("DNS PrefetchFound"); |
147 } | 176 } |
148 | 177 |
149 void UrlInfo::SetNoSuchNameState() { | 178 void UrlInfo::SetNoSuchNameState() { |
150 DCHECK(ASSIGNED == state_); | 179 DCHECK(ASSIGNED == state_); |
151 state_ = NO_SUCH_NAME; | 180 state_ = NO_SUCH_NAME; |
152 resolve_duration_ = GetDuration(); | 181 resolve_duration_ = GetDuration(); |
153 if (kMaxNonNetworkDnsLookupDuration <= resolve_duration_) { | 182 if (MaxNonNetworkDnsLookupDuration() <= resolve_duration_) { |
154 DHISTOGRAM_TIMES("DNS.PrefetchNotFoundName", resolve_duration_); | 183 DHISTOGRAM_TIMES("DNS.PrefetchNotFoundName", resolve_duration_); |
155 } | 184 } |
156 sequence_number_ = sequence_counter++; | 185 sequence_number_ = sequence_counter++; |
157 DLogResultsStats("DNS PrefetchNotFound"); | 186 DLogResultsStats("DNS PrefetchNotFound"); |
158 } | 187 } |
159 | 188 |
160 void UrlInfo::SetUrl(const GURL& url) { | 189 void UrlInfo::SetUrl(const GURL& url) { |
161 if (url_.is_empty()) // Not yet initialized. | 190 if (url_.is_empty()) // Not yet initialized. |
162 url_ = url; | 191 url_ = url; |
163 else | 192 else |
164 DCHECK_EQ(url_, url); | 193 DCHECK_EQ(url_, url); |
165 } | 194 } |
166 | 195 |
167 // IsStillCached() guesses if the DNS cache still has IP data, | 196 // IsStillCached() guesses if the DNS cache still has IP data, |
168 // or at least remembers results about "not finding host." | 197 // or at least remembers results about "not finding host." |
169 bool UrlInfo::IsStillCached() const { | 198 bool UrlInfo::IsStillCached() const { |
170 DCHECK(FOUND == state_ || NO_SUCH_NAME == state_); | 199 DCHECK(FOUND == state_ || NO_SUCH_NAME == state_); |
171 | 200 |
172 // Default MS OS does not cache failures. Hence we could return false almost | 201 // 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 | 202 // 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 | 203 // the value if we returned false that way. Hence we'll just let the lookup |
175 // time out the same way as FOUND case. | 204 // time out the same way as FOUND case. |
176 | 205 |
177 if (sequence_counter - sequence_number_ > kMaxGuaranteedDnsCacheSize) | 206 if (sequence_counter - sequence_number_ > kMaxGuaranteedDnsCacheSize) |
178 return false; | 207 return false; |
179 | 208 |
180 TimeDelta time_since_resolution = TimeTicks::Now() - time_; | 209 const TimeDelta time_since_resolution = TimeTicks::Now() - time_; |
181 | 210 return time_since_resolution < global_state.Get().cache_expiration_duration; |
182 return time_since_resolution < cache_expiration_duration_; | |
183 } | 211 } |
184 | 212 |
185 void UrlInfo::DLogResultsStats(const char* message) const { | 213 void UrlInfo::DLogResultsStats(const char* message) const { |
186 if (!detailed_logging_enabled) | 214 if (!detailed_logging_enabled) |
187 return; | 215 return; |
188 DVLOG(1) << "\t" << message << "\tq=" << queue_duration().InMilliseconds() | 216 DVLOG(1) << "\t" << message << "\tq=" << queue_duration().InMilliseconds() |
189 << "ms,\tr=" << resolve_duration().InMilliseconds() | 217 << "ms,\tr=" << resolve_duration().InMilliseconds() |
190 << "ms,\tp=" << sequence_number_ << "\t" << url_.spec(); | 218 << "ms,\tp=" << sequence_number_ << "\t" << url_.spec(); |
191 } | 219 } |
192 | 220 |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
341 | 369 |
342 case LEARNED_REFERAL_MOTIVATED: | 370 case LEARNED_REFERAL_MOTIVATED: |
343 return RemoveJs(referring_url_.spec()); | 371 return RemoveJs(referring_url_.spec()); |
344 | 372 |
345 default: | 373 default: |
346 return ""; | 374 return ""; |
347 } | 375 } |
348 } | 376 } |
349 | 377 |
350 } // namespace chrome_browser_net | 378 } // namespace chrome_browser_net |
OLD | NEW |