NaCl: Update revision in DEPS, r12770 -> r12773
[chromium-blink-merge.git] / chrome / browser / net / spdyproxy / data_saving_metrics.cc
blobee26d4d022ed04a3d4b625311737e481d5a0fd60
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/net/spdyproxy/data_saving_metrics.h"
7 #include "base/metrics/histogram.h"
8 #include "base/prefs/pref_service.h"
9 #include "base/prefs/scoped_user_pref_update.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "chrome/browser/net/spdyproxy/data_reduction_proxy_settings.h"
13 #include "chrome/common/pref_names.h"
14 #include "content/public/common/url_constants.h"
15 #include "net/base/host_port_pair.h"
16 #include "net/http/http_response_headers.h"
17 #include "net/proxy/proxy_retry_info.h"
18 #include "net/proxy/proxy_service.h"
19 #include "net/url_request/url_request_context.h"
21 namespace {
23 // A bypass delay more than this is treated as a long delay.
24 const int kLongBypassDelayInSeconds = 30 * 60;
26 #if defined(OS_ANDROID) || defined(OS_IOS)
28 // The number of days of history stored in the content lengths prefs.
29 const size_t kNumDaysInHistory = 60;
31 // Increments an int64, stored as a string, in a ListPref at the specified
32 // index. The value must already exist and be a string representation of a
33 // number.
34 void AddInt64ToListPref(size_t index,
35 int64 length,
36 base::ListValue* list_update) {
37 int64 value = 0;
38 std::string old_string_value;
39 bool rv = list_update->GetString(index, &old_string_value);
40 DCHECK(rv);
41 if (rv) {
42 rv = base::StringToInt64(old_string_value, &value);
43 DCHECK(rv);
45 value += length;
46 list_update->Set(index,
47 base::Value::CreateStringValue(base::Int64ToString(value)));
50 int64 ListPrefInt64Value(const base::ListValue& list_update, size_t index) {
51 std::string string_value;
52 if (!list_update.GetString(index, &string_value)) {
53 NOTREACHED();
54 return 0;
57 int64 value = 0;
58 bool rv = base::StringToInt64(string_value, &value);
59 DCHECK(rv);
60 return value;
63 // Report UMA metrics for daily data reductions.
64 void RecordDailyContentLengthHistograms(
65 int64 original_length,
66 int64 received_length,
67 int64 original_length_with_data_reduction_enabled,
68 int64 received_length_with_data_reduction_enabled,
69 int64 original_length_via_data_reduction_proxy,
70 int64 received_length_via_data_reduction_proxy,
71 int64 https_length_with_data_reduction_enabled,
72 int64 short_bypass_length_with_data_reduction_enabled,
73 int64 long_bypass_length_with_data_reduction_enabled,
74 int64 unknown_length_with_data_reduction_enabled) {
75 // Report daily UMA only for days having received content.
76 if (original_length <= 0 || received_length <= 0)
77 return;
79 // Record metrics in KB.
80 UMA_HISTOGRAM_COUNTS(
81 "Net.DailyOriginalContentLength", original_length >> 10);
82 UMA_HISTOGRAM_COUNTS(
83 "Net.DailyContentLength", received_length >> 10);
84 int percent = 0;
85 // UMA percentage cannot be negative.
86 if (original_length > received_length) {
87 percent = (100 * (original_length - received_length)) / original_length;
89 UMA_HISTOGRAM_PERCENTAGE("Net.DailyContentSavingPercent", percent);
91 if (original_length_with_data_reduction_enabled <= 0 ||
92 received_length_with_data_reduction_enabled <= 0) {
93 return;
96 UMA_HISTOGRAM_COUNTS(
97 "Net.DailyOriginalContentLength_DataReductionProxyEnabled",
98 original_length_with_data_reduction_enabled >> 10);
99 UMA_HISTOGRAM_COUNTS(
100 "Net.DailyContentLength_DataReductionProxyEnabled",
101 received_length_with_data_reduction_enabled >> 10);
103 int percent_data_reduction_proxy_enabled = 0;
104 // UMA percentage cannot be negative.
105 if (original_length_with_data_reduction_enabled >
106 received_length_with_data_reduction_enabled) {
107 percent_data_reduction_proxy_enabled =
108 100 * (original_length_with_data_reduction_enabled -
109 received_length_with_data_reduction_enabled) /
110 original_length_with_data_reduction_enabled;
112 UMA_HISTOGRAM_PERCENTAGE(
113 "Net.DailyContentSavingPercent_DataReductionProxyEnabled",
114 percent_data_reduction_proxy_enabled);
116 UMA_HISTOGRAM_PERCENTAGE(
117 "Net.DailyContentPercent_DataReductionProxyEnabled",
118 (100 * received_length_with_data_reduction_enabled) / received_length);
120 if (https_length_with_data_reduction_enabled > 0) {
121 UMA_HISTOGRAM_COUNTS(
122 "Net.DailyContentLength_DataReductionProxyEnabled_Https",
123 https_length_with_data_reduction_enabled >> 10);
124 UMA_HISTOGRAM_PERCENTAGE(
125 "Net.DailyContentPercent_DataReductionProxyEnabled_Https",
126 (100 * https_length_with_data_reduction_enabled) / received_length);
129 if (short_bypass_length_with_data_reduction_enabled > 0) {
130 UMA_HISTOGRAM_COUNTS(
131 "Net.DailyContentLength_DataReductionProxyEnabled_ShortBypass",
132 short_bypass_length_with_data_reduction_enabled >> 10);
133 UMA_HISTOGRAM_PERCENTAGE(
134 "Net.DailyContentPercent_DataReductionProxyEnabled_ShortBypass",
135 ((100 * short_bypass_length_with_data_reduction_enabled) /
136 received_length));
139 if (long_bypass_length_with_data_reduction_enabled > 0) {
140 UMA_HISTOGRAM_COUNTS(
141 "Net.DailyContentLength_DataReductionProxyEnabled_LongBypass",
142 long_bypass_length_with_data_reduction_enabled >> 10);
143 UMA_HISTOGRAM_PERCENTAGE(
144 "Net.DailyContentPercent_DataReductionProxyEnabled_LongBypass",
145 ((100 * long_bypass_length_with_data_reduction_enabled) /
146 received_length));
149 if (unknown_length_with_data_reduction_enabled > 0) {
150 UMA_HISTOGRAM_COUNTS(
151 "Net.DailyContentLength_DataReductionProxyEnabled_Unknown",
152 unknown_length_with_data_reduction_enabled >> 10);
153 UMA_HISTOGRAM_PERCENTAGE(
154 "Net.DailyContentPercent_DataReductionProxyEnabled_Unknown",
155 ((100 * unknown_length_with_data_reduction_enabled) /
156 received_length));
159 if (original_length_via_data_reduction_proxy <= 0 ||
160 received_length_via_data_reduction_proxy <= 0) {
161 return;
164 UMA_HISTOGRAM_COUNTS(
165 "Net.DailyOriginalContentLength_ViaDataReductionProxy",
166 original_length_via_data_reduction_proxy >> 10);
167 UMA_HISTOGRAM_COUNTS(
168 "Net.DailyContentLength_ViaDataReductionProxy",
169 received_length_via_data_reduction_proxy >> 10);
170 int percent_via_data_reduction_proxy = 0;
171 if (original_length_via_data_reduction_proxy >
172 received_length_via_data_reduction_proxy) {
173 percent_via_data_reduction_proxy =
174 100 * (original_length_via_data_reduction_proxy -
175 received_length_via_data_reduction_proxy) /
176 original_length_via_data_reduction_proxy;
178 UMA_HISTOGRAM_PERCENTAGE(
179 "Net.DailyContentSavingPercent_ViaDataReductionProxy",
180 percent_via_data_reduction_proxy);
181 UMA_HISTOGRAM_PERCENTAGE(
182 "Net.DailyContentPercent_ViaDataReductionProxy",
183 (100 * received_length_via_data_reduction_proxy) / received_length);
186 // Ensure list has exactly |length| elements, either by truncating at the
187 // front, or appending "0"'s to the back.
188 void MaintainContentLengthPrefsWindow(base::ListValue* list, size_t length) {
189 // Remove data for old days from the front.
190 while (list->GetSize() > length)
191 list->Remove(0, NULL);
192 // Newly added lists are empty. Add entries to back to fill the window,
193 // each initialized to zero.
194 while (list->GetSize() < length)
195 list->AppendString(base::Int64ToString(0));
196 DCHECK_EQ(length, list->GetSize());
199 // DailyContentLengthUpdate maintains a data saving pref. The pref is a list
200 // of |kNumDaysInHistory| elements of daily total content lengths for the past
201 // |kNumDaysInHistory| days.
202 class DailyContentLengthUpdate {
203 public:
204 DailyContentLengthUpdate(
205 const char* pref,
206 PrefService* pref_service)
207 : update_(pref_service, pref) {
210 void UpdateForDataChange(int days_since_last_update) {
211 // New empty lists may have been created. Maintain the invariant that
212 // there should be exactly |kNumDaysInHistory| days in the histories.
213 MaintainContentLengthPrefsWindow(update_.Get(), kNumDaysInHistory);
214 if (days_since_last_update) {
215 MaintainContentLengthPrefForDateChange(days_since_last_update);
219 // Update the lengths for the current day.
220 void Add(int content_length) {
221 AddInt64ToListPref(kNumDaysInHistory - 1, content_length, update_.Get());
224 int64 GetListPrefValue(size_t index) {
225 return ListPrefInt64Value(*update_, index);
228 private:
229 // Update the list for date change and ensure the list has exactly |length|
230 // elements. The last entry in the list will be for the current day after
231 // the update.
232 void MaintainContentLengthPrefForDateChange(int days_since_last_update) {
233 if (days_since_last_update == -1) {
234 // The system may go backwards in time by up to a day for legitimate
235 // reasons, such as with changes to the time zone. In such cases, we
236 // keep adding to the current day.
237 // Note: we accept the fact that some reported data is shifted to
238 // the adjacent day if users travel back and forth across time zones.
239 days_since_last_update = 0;
240 } else if (days_since_last_update < -1) {
241 // Erase all entries if the system went backwards in time by more than
242 // a day.
243 update_->Clear();
245 days_since_last_update = kNumDaysInHistory;
247 DCHECK_GE(days_since_last_update, 0);
249 // Add entries for days since last update event. This will make the
250 // lists longer than kNumDaysInHistory. The additional items will be cut off
251 // from the head of the lists by |MaintainContentLengthPrefsWindow|, below.
252 for (int i = 0;
253 i < days_since_last_update && i < static_cast<int>(kNumDaysInHistory);
254 ++i) {
255 update_->AppendString(base::Int64ToString(0));
258 // Entries for new days may have been appended. Maintain the invariant that
259 // there should be exactly |kNumDaysInHistory| days in the histories.
260 MaintainContentLengthPrefsWindow(update_.Get(), kNumDaysInHistory);
263 ListPrefUpdate update_;
266 // DailyDataSavingUpdate maintains a pair of data saving prefs, original_update_
267 // and received_update_. pref_original is a list of |kNumDaysInHistory| elements
268 // of daily total original content lengths for the past |kNumDaysInHistory|
269 // days. pref_received is the corresponding list of the daily total received
270 // content lengths.
271 class DailyDataSavingUpdate {
272 public:
273 DailyDataSavingUpdate(
274 const char* pref_original,
275 const char* pref_received,
276 PrefService* pref_service)
277 : original_(pref_original, pref_service),
278 received_(pref_received, pref_service) {
281 void UpdateForDataChange(int days_since_last_update) {
282 original_.UpdateForDataChange(days_since_last_update);
283 received_.UpdateForDataChange(days_since_last_update);
286 // Update the lengths for the current day.
287 void Add(int original_content_length, int received_content_length) {
288 original_.Add(original_content_length);
289 received_.Add(received_content_length);
292 int64 GetOriginalListPrefValue(size_t index) {
293 return original_.GetListPrefValue(index);
295 int64 GetReceivedListPrefValue(size_t index) {
296 return received_.GetListPrefValue(index);
299 private:
300 DailyContentLengthUpdate original_;
301 DailyContentLengthUpdate received_;
304 #endif // defined(OS_ANDROID) || defined(OS_IOS)
306 // Returns true if the request is bypassed by all configured data reduction
307 // proxies. It returns the bypass delay in delay_seconds (if not NULL). If
308 // the request is bypassed by more than one proxy, delay_seconds returns
309 // shortest delay.
310 bool IsBypassRequest(const net::URLRequest* request, int64* delay_seconds) {
311 #if defined(OS_ANDROID) || defined(OS_IOS)
312 DataReductionProxySettings::DataReductionProxyList proxies =
313 DataReductionProxySettings::GetDataReductionProxies();
314 if (proxies.size() == 0)
315 return false;
317 if (request == NULL || request->context() == NULL ||
318 request->context()->proxy_service() == NULL) {
319 return false;
322 const net::ProxyRetryInfoMap& retry_map =
323 request->context()->proxy_service()->proxy_retry_info();
324 if (retry_map.size() == 0)
325 return false;
327 int64 shortest_delay = 0;
328 // The request is bypassed if all configured proxies are in the retry map.
329 for (size_t i = 0; i < proxies.size(); ++i) {
330 std::string proxy = net::HostPortPair::FromURL(proxies[i]).ToString();
331 // The retry list has the scheme prefix for https but not for http.
332 if (proxies[i].SchemeIs(content::kHttpsScheme))
333 proxy = std::string(content::kHttpsScheme) + "://" + proxy;
335 net::ProxyRetryInfoMap::const_iterator found = retry_map.find(proxy);
336 if (found == retry_map.end())
337 return false;
338 if (shortest_delay == 0 ||
339 shortest_delay > found->second.current_delay.InSeconds()) {
340 shortest_delay = found->second.current_delay.InSeconds();
343 if (delay_seconds != NULL)
344 *delay_seconds = shortest_delay;
345 return true;
346 #else
347 return false;
348 #endif // defined(OS_ANDROID) || defined(OS_IOS)
351 } // namespace
353 namespace spdyproxy {
355 DataReductionRequestType GetDataReductionRequestType(
356 const net::URLRequest* request) {
357 if (request->url().SchemeIs(content::kHttpsScheme))
358 return HTTPS;
359 if (!request->url().SchemeIs(content::kHttpScheme)) {
360 NOTREACHED();
361 return UNKNOWN_TYPE;
363 int64 bypass_delay = 0;
364 if (IsBypassRequest(request, &bypass_delay)) {
365 return (bypass_delay > kLongBypassDelayInSeconds) ?
366 LONG_BYPASS : SHORT_BYPASS;
368 #if defined(SPDY_PROXY_AUTH_ORIGIN)
369 if (request->response_info().headers &&
370 request->response_info().headers->IsChromeProxyResponse()) {
371 return VIA_DATA_REDUCTION_PROXY;
373 #endif
374 return UNKNOWN_TYPE;
377 int64 GetAdjustedOriginalContentLength(
378 DataReductionRequestType data_reduction_type,
379 int64 original_content_length,
380 int64 received_content_length) {
381 // Since there was no indication of the original content length, presume
382 // it is no different from the number of bytes read.
383 if (original_content_length == -1 ||
384 data_reduction_type != spdyproxy::VIA_DATA_REDUCTION_PROXY) {
385 return received_content_length;
387 return original_content_length;
390 #if defined(OS_ANDROID) || defined(OS_IOS)
391 void UpdateContentLengthPrefsForDataReductionProxy(
392 int received_content_length,
393 int original_content_length,
394 bool with_data_reduction_proxy_enabled,
395 DataReductionRequestType data_reduction_type,
396 base::Time now, PrefService* prefs) {
397 // TODO(bengr): Remove this check once the underlying cause of
398 // http://crbug.com/287821 is fixed. For now, only continue if the current
399 // year is reported as being between 1972 and 2970.
400 base::TimeDelta time_since_unix_epoch = now - base::Time::UnixEpoch();
401 const int kMinDaysSinceUnixEpoch = 365 * 2; // 2 years.
402 const int kMaxDaysSinceUnixEpoch = 365 * 1000; // 1000 years.
403 if (time_since_unix_epoch.InDays() < kMinDaysSinceUnixEpoch ||
404 time_since_unix_epoch.InDays() > kMaxDaysSinceUnixEpoch) {
405 return;
408 // Determine how many days it has been since the last update.
409 int64 then_internal = prefs->GetInt64(
410 prefs::kDailyHttpContentLengthLastUpdateDate);
411 // Local midnight could have been shifted due to time zone change.
412 base::Time then_midnight =
413 base::Time::FromInternalValue(then_internal).LocalMidnight();
414 base::Time midnight = now.LocalMidnight();
415 int days_since_last_update = (midnight - then_midnight).InDays();
417 // Each day, we calculate the total number of bytes received and the total
418 // size of all corresponding resources before any data-reducing recompression
419 // is applied. These values are used to compute the data savings realized
420 // by applying our compression techniques. Totals for the last
421 // |kNumDaysInHistory| days are maintained.
422 DailyDataSavingUpdate total(
423 prefs::kDailyHttpOriginalContentLength,
424 prefs::kDailyHttpReceivedContentLength,
425 prefs);
426 total.UpdateForDataChange(days_since_last_update);
428 DailyDataSavingUpdate proxy_enabled(
429 prefs::kDailyOriginalContentLengthWithDataReductionProxyEnabled,
430 prefs::kDailyContentLengthWithDataReductionProxyEnabled,
431 prefs);
432 proxy_enabled.UpdateForDataChange(days_since_last_update);
434 DailyDataSavingUpdate via_proxy(
435 prefs::kDailyOriginalContentLengthViaDataReductionProxy,
436 prefs::kDailyContentLengthViaDataReductionProxy,
437 prefs);
438 via_proxy.UpdateForDataChange(days_since_last_update);
440 DailyContentLengthUpdate https(
441 prefs::kDailyContentLengthHttpsWithDataReductionProxyEnabled, prefs);
442 https.UpdateForDataChange(days_since_last_update);
444 DailyContentLengthUpdate short_bypass(
445 prefs::kDailyContentLengthShortBypassWithDataReductionProxyEnabled,
446 prefs);
447 short_bypass.UpdateForDataChange(days_since_last_update);
449 DailyContentLengthUpdate long_bypass(
450 prefs::kDailyContentLengthLongBypassWithDataReductionProxyEnabled, prefs);
451 long_bypass.UpdateForDataChange(days_since_last_update);
453 DailyContentLengthUpdate unknown(
454 prefs::kDailyContentLengthUnknownWithDataReductionProxyEnabled, prefs);
455 unknown.UpdateForDataChange(days_since_last_update);
457 total.Add(original_content_length, received_content_length);
458 if (with_data_reduction_proxy_enabled) {
459 proxy_enabled.Add(original_content_length, received_content_length);
460 // Ignore data source cases, if exist, when
461 // "with_data_reduction_proxy_enabled == false"
462 switch (data_reduction_type) {
463 case VIA_DATA_REDUCTION_PROXY:
464 via_proxy.Add(original_content_length, received_content_length);
465 break;
466 case HTTPS:
467 https.Add(received_content_length);
468 break;
469 case SHORT_BYPASS:
470 short_bypass.Add(received_content_length);
471 break;
472 case LONG_BYPASS:
473 long_bypass.Add(received_content_length);
474 break;
475 case UNKNOWN_TYPE:
476 unknown.Add(received_content_length);
477 break;
481 if (days_since_last_update) {
482 // Record the last update time in microseconds in UTC.
483 prefs->SetInt64(prefs::kDailyHttpContentLengthLastUpdateDate,
484 midnight.ToInternalValue());
486 // A new day. Report the previous day's data if exists. We'll lose usage
487 // data if the last time Chrome was run was more than a day ago.
488 // Here, we prefer collecting less data but the collected data is
489 // associated with an accurate date.
490 if (days_since_last_update == 1) {
491 // The previous day's data point is the second one from the tail.
492 // Therefore (kNumDaysInHistory - 2) below.
493 RecordDailyContentLengthHistograms(
494 total.GetOriginalListPrefValue(kNumDaysInHistory - 2),
495 total.GetReceivedListPrefValue(kNumDaysInHistory - 2),
496 proxy_enabled.GetOriginalListPrefValue(kNumDaysInHistory - 2),
497 proxy_enabled.GetReceivedListPrefValue(kNumDaysInHistory - 2),
498 via_proxy.GetOriginalListPrefValue(kNumDaysInHistory - 2),
499 via_proxy.GetReceivedListPrefValue(kNumDaysInHistory - 2),
500 https.GetListPrefValue(kNumDaysInHistory - 2),
501 short_bypass.GetListPrefValue(kNumDaysInHistory - 2),
502 long_bypass.GetListPrefValue(kNumDaysInHistory - 2),
503 unknown.GetListPrefValue(kNumDaysInHistory - 2));
507 #endif // defined(OS_ANDROID) || defined(OS_IOS)
509 void UpdateContentLengthPrefs(
510 int received_content_length,
511 int original_content_length,
512 bool with_data_reduction_proxy_enabled,
513 DataReductionRequestType data_reduction_type,
514 PrefService* prefs) {
515 int64 total_received = prefs->GetInt64(prefs::kHttpReceivedContentLength);
516 int64 total_original = prefs->GetInt64(prefs::kHttpOriginalContentLength);
517 total_received += received_content_length;
518 total_original += original_content_length;
519 prefs->SetInt64(prefs::kHttpReceivedContentLength, total_received);
520 prefs->SetInt64(prefs::kHttpOriginalContentLength, total_original);
522 #if defined(OS_ANDROID) || defined(OS_IOS)
523 UpdateContentLengthPrefsForDataReductionProxy(
524 received_content_length,
525 original_content_length,
526 with_data_reduction_proxy_enabled,
527 data_reduction_type,
528 base::Time::Now(),
529 prefs);
530 #endif // defined(OS_ANDROID) || defined(OS_IOS)
534 } // namespace spdyproxy