1 // Copyright 2014 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 "components/data_reduction_proxy/browser/data_reduction_proxy_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 "components/data_reduction_proxy/browser/data_reduction_proxy_settings.h"
13 #include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h"
14 #include "components/data_reduction_proxy/common/data_reduction_proxy_pref_names.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 data_reduction_proxy
{
25 #if defined(SPDY_PROXY_AUTH_ORIGIN)
26 // A bypass delay more than this is treated as a long delay.
27 const int kLongBypassDelayInSeconds
= 30 * 60;
30 // Increments an int64, stored as a string, in a ListPref at the specified
31 // index. The value must already exist and be a string representation of a
33 void AddInt64ToListPref(size_t index
,
35 base::ListValue
* list_update
) {
37 std::string old_string_value
;
38 bool rv
= list_update
->GetString(index
, &old_string_value
);
41 rv
= base::StringToInt64(old_string_value
, &value
);
45 list_update
->Set(index
, new base::StringValue(base::Int64ToString(value
)));
48 int64
ListPrefInt64Value(const base::ListValue
& list_update
, size_t index
) {
49 std::string string_value
;
50 if (!list_update
.GetString(index
, &string_value
)) {
56 bool rv
= base::StringToInt64(string_value
, &value
);
61 // Report UMA metrics for daily data reductions.
62 void RecordDailyContentLengthHistograms(
63 int64 original_length
,
64 int64 received_length
,
65 int64 original_length_with_data_reduction_enabled
,
66 int64 received_length_with_data_reduction_enabled
,
67 int64 original_length_via_data_reduction_proxy
,
68 int64 received_length_via_data_reduction_proxy
,
69 int64 https_length_with_data_reduction_enabled
,
70 int64 short_bypass_length_with_data_reduction_enabled
,
71 int64 long_bypass_length_with_data_reduction_enabled
,
72 int64 unknown_length_with_data_reduction_enabled
) {
73 // Report daily UMA only for days having received content.
74 if (original_length
<= 0 || received_length
<= 0)
77 // Record metrics in KB.
79 "Net.DailyOriginalContentLength", original_length
>> 10);
81 "Net.DailyContentLength", received_length
>> 10);
83 // UMA percentage cannot be negative.
84 if (original_length
> received_length
) {
85 percent
= (100 * (original_length
- received_length
)) / original_length
;
87 UMA_HISTOGRAM_PERCENTAGE("Net.DailyContentSavingPercent", percent
);
89 if (original_length_with_data_reduction_enabled
<= 0 ||
90 received_length_with_data_reduction_enabled
<= 0) {
95 "Net.DailyOriginalContentLength_DataReductionProxyEnabled",
96 original_length_with_data_reduction_enabled
>> 10);
98 "Net.DailyContentLength_DataReductionProxyEnabled",
99 received_length_with_data_reduction_enabled
>> 10);
101 int percent_data_reduction_proxy_enabled
= 0;
102 // UMA percentage cannot be negative.
103 if (original_length_with_data_reduction_enabled
>
104 received_length_with_data_reduction_enabled
) {
105 percent_data_reduction_proxy_enabled
=
106 100 * (original_length_with_data_reduction_enabled
-
107 received_length_with_data_reduction_enabled
) /
108 original_length_with_data_reduction_enabled
;
110 UMA_HISTOGRAM_PERCENTAGE(
111 "Net.DailyContentSavingPercent_DataReductionProxyEnabled",
112 percent_data_reduction_proxy_enabled
);
114 UMA_HISTOGRAM_PERCENTAGE(
115 "Net.DailyContentPercent_DataReductionProxyEnabled",
116 (100 * received_length_with_data_reduction_enabled
) / received_length
);
118 DCHECK_GE(https_length_with_data_reduction_enabled
, 0);
119 UMA_HISTOGRAM_COUNTS(
120 "Net.DailyContentLength_DataReductionProxyEnabled_Https",
121 https_length_with_data_reduction_enabled
>> 10);
122 UMA_HISTOGRAM_PERCENTAGE(
123 "Net.DailyContentPercent_DataReductionProxyEnabled_Https",
124 (100 * https_length_with_data_reduction_enabled
) / received_length
);
126 DCHECK_GE(short_bypass_length_with_data_reduction_enabled
, 0);
127 UMA_HISTOGRAM_COUNTS(
128 "Net.DailyContentLength_DataReductionProxyEnabled_ShortBypass",
129 short_bypass_length_with_data_reduction_enabled
>> 10);
130 UMA_HISTOGRAM_PERCENTAGE(
131 "Net.DailyContentPercent_DataReductionProxyEnabled_ShortBypass",
132 ((100 * short_bypass_length_with_data_reduction_enabled
) /
135 DCHECK_GE(long_bypass_length_with_data_reduction_enabled
, 0);
136 UMA_HISTOGRAM_COUNTS(
137 "Net.DailyContentLength_DataReductionProxyEnabled_LongBypass",
138 long_bypass_length_with_data_reduction_enabled
>> 10);
139 UMA_HISTOGRAM_PERCENTAGE(
140 "Net.DailyContentPercent_DataReductionProxyEnabled_LongBypass",
141 ((100 * long_bypass_length_with_data_reduction_enabled
) /
144 DCHECK_GE(unknown_length_with_data_reduction_enabled
, 0);
145 UMA_HISTOGRAM_COUNTS(
146 "Net.DailyContentLength_DataReductionProxyEnabled_Unknown",
147 unknown_length_with_data_reduction_enabled
>> 10);
148 UMA_HISTOGRAM_PERCENTAGE(
149 "Net.DailyContentPercent_DataReductionProxyEnabled_Unknown",
150 ((100 * unknown_length_with_data_reduction_enabled
) /
153 DCHECK_GE(original_length_via_data_reduction_proxy
, 0);
154 UMA_HISTOGRAM_COUNTS(
155 "Net.DailyOriginalContentLength_ViaDataReductionProxy",
156 original_length_via_data_reduction_proxy
>> 10);
157 DCHECK_GE(received_length_via_data_reduction_proxy
, 0);
158 UMA_HISTOGRAM_COUNTS(
159 "Net.DailyContentLength_ViaDataReductionProxy",
160 received_length_via_data_reduction_proxy
>> 10);
161 int percent_via_data_reduction_proxy
= 0;
162 if (original_length_via_data_reduction_proxy
>
163 received_length_via_data_reduction_proxy
) {
164 percent_via_data_reduction_proxy
=
165 100 * (original_length_via_data_reduction_proxy
-
166 received_length_via_data_reduction_proxy
) /
167 original_length_via_data_reduction_proxy
;
169 UMA_HISTOGRAM_PERCENTAGE(
170 "Net.DailyContentSavingPercent_ViaDataReductionProxy",
171 percent_via_data_reduction_proxy
);
172 UMA_HISTOGRAM_PERCENTAGE(
173 "Net.DailyContentPercent_ViaDataReductionProxy",
174 (100 * received_length_via_data_reduction_proxy
) / received_length
);
177 // Ensure list has exactly |length| elements, either by truncating at the
178 // front, or appending "0"'s to the back.
179 void MaintainContentLengthPrefsWindow(base::ListValue
* list
, size_t length
) {
180 // Remove data for old days from the front.
181 while (list
->GetSize() > length
)
182 list
->Remove(0, NULL
);
183 // Newly added lists are empty. Add entries to back to fill the window,
184 // each initialized to zero.
185 while (list
->GetSize() < length
)
186 list
->AppendString(base::Int64ToString(0));
187 DCHECK_EQ(length
, list
->GetSize());
190 // DailyContentLengthUpdate maintains a data saving pref. The pref is a list
191 // of |kNumDaysInHistory| elements of daily total content lengths for the past
192 // |kNumDaysInHistory| days.
193 class DailyContentLengthUpdate
{
195 DailyContentLengthUpdate(
197 PrefService
* pref_service
)
198 : update_(pref_service
, pref
) {
201 void UpdateForDataChange(int days_since_last_update
) {
202 // New empty lists may have been created. Maintain the invariant that
203 // there should be exactly |kNumDaysInHistory| days in the histories.
204 MaintainContentLengthPrefsWindow(update_
.Get(), kNumDaysInHistory
);
205 if (days_since_last_update
) {
206 MaintainContentLengthPrefForDateChange(days_since_last_update
);
210 // Update the lengths for the current day.
211 void Add(int content_length
) {
212 AddInt64ToListPref(kNumDaysInHistory
- 1, content_length
, update_
.Get());
215 int64
GetListPrefValue(size_t index
) {
216 return ListPrefInt64Value(*update_
, index
);
220 // Update the list for date change and ensure the list has exactly |length|
221 // elements. The last entry in the list will be for the current day after
223 void MaintainContentLengthPrefForDateChange(int days_since_last_update
) {
224 if (days_since_last_update
== -1) {
225 // The system may go backwards in time by up to a day for legitimate
226 // reasons, such as with changes to the time zone. In such cases, we
227 // keep adding to the current day.
228 // Note: we accept the fact that some reported data is shifted to
229 // the adjacent day if users travel back and forth across time zones.
230 days_since_last_update
= 0;
231 } else if (days_since_last_update
< -1) {
232 // Erase all entries if the system went backwards in time by more than
236 days_since_last_update
= kNumDaysInHistory
;
238 DCHECK_GE(days_since_last_update
, 0);
240 // Add entries for days since last update event. This will make the
241 // lists longer than kNumDaysInHistory. The additional items will be cut off
242 // from the head of the lists by |MaintainContentLengthPrefsWindow|, below.
244 i
< days_since_last_update
&& i
< static_cast<int>(kNumDaysInHistory
);
246 update_
->AppendString(base::Int64ToString(0));
249 // Entries for new days may have been appended. Maintain the invariant that
250 // there should be exactly |kNumDaysInHistory| days in the histories.
251 MaintainContentLengthPrefsWindow(update_
.Get(), kNumDaysInHistory
);
254 ListPrefUpdate update_
;
257 // DailyDataSavingUpdate maintains a pair of data saving prefs, original_update_
258 // and received_update_. pref_original is a list of |kNumDaysInHistory| elements
259 // of daily total original content lengths for the past |kNumDaysInHistory|
260 // days. pref_received is the corresponding list of the daily total received
262 class DailyDataSavingUpdate
{
264 DailyDataSavingUpdate(
265 const char* pref_original
,
266 const char* pref_received
,
267 PrefService
* pref_service
)
268 : original_(pref_original
, pref_service
),
269 received_(pref_received
, pref_service
) {
272 void UpdateForDataChange(int days_since_last_update
) {
273 original_
.UpdateForDataChange(days_since_last_update
);
274 received_
.UpdateForDataChange(days_since_last_update
);
277 // Update the lengths for the current day.
278 void Add(int original_content_length
, int received_content_length
) {
279 original_
.Add(original_content_length
);
280 received_
.Add(received_content_length
);
283 int64
GetOriginalListPrefValue(size_t index
) {
284 return original_
.GetListPrefValue(index
);
286 int64
GetReceivedListPrefValue(size_t index
) {
287 return received_
.GetListPrefValue(index
);
291 DailyContentLengthUpdate original_
;
292 DailyContentLengthUpdate received_
;
297 DataReductionProxyRequestType
GetDataReductionProxyRequestType(
298 const net::URLRequest
* request
) {
299 if (request
->url().SchemeIs("https"))
301 if (!request
->url().SchemeIs("http")) {
305 #if defined(SPDY_PROXY_AUTH_ORIGIN)
306 DataReductionProxyParams
params(
307 DataReductionProxyParams::kAllowed
|
308 DataReductionProxyParams::kFallbackAllowed
|
309 DataReductionProxyParams::kPromoAllowed
);
310 base::TimeDelta bypass_delay
;
311 if (params
.AreDataReductionProxiesBypassed(*request
, &bypass_delay
)) {
312 if (bypass_delay
> base::TimeDelta::FromSeconds(kLongBypassDelayInSeconds
))
317 if (request
->response_info().headers
&&
318 HasDataReductionProxyViaHeader(request
->response_info().headers
, NULL
)) {
319 return VIA_DATA_REDUCTION_PROXY
;
324 int64
GetAdjustedOriginalContentLength(
325 DataReductionProxyRequestType request_type
,
326 int64 original_content_length
,
327 int64 received_content_length
) {
328 // Since there was no indication of the original content length, presume
329 // it is no different from the number of bytes read.
330 if (original_content_length
== -1 ||
331 request_type
!= VIA_DATA_REDUCTION_PROXY
) {
332 return received_content_length
;
334 return original_content_length
;
337 void UpdateContentLengthPrefsForDataReductionProxy(
338 int received_content_length
,
339 int original_content_length
,
340 bool with_data_reduction_proxy_enabled
,
341 DataReductionProxyRequestType request_type
,
342 base::Time now
, PrefService
* prefs
) {
343 // TODO(bengr): Remove this check once the underlying cause of
344 // http://crbug.com/287821 is fixed. For now, only continue if the current
345 // year is reported as being between 1972 and 2970.
346 base::TimeDelta time_since_unix_epoch
= now
- base::Time::UnixEpoch();
347 const int kMinDaysSinceUnixEpoch
= 365 * 2; // 2 years.
348 const int kMaxDaysSinceUnixEpoch
= 365 * 1000; // 1000 years.
349 if (time_since_unix_epoch
.InDays() < kMinDaysSinceUnixEpoch
||
350 time_since_unix_epoch
.InDays() > kMaxDaysSinceUnixEpoch
) {
354 // Determine how many days it has been since the last update.
355 int64 then_internal
= prefs
->GetInt64(
356 data_reduction_proxy::prefs::kDailyHttpContentLengthLastUpdateDate
);
359 base::Time then_midnight
= base::Time::FromInternalValue(then_internal
);
360 base::Time midnight
=
361 base::Time::FromInternalValue(
362 (now
.ToInternalValue() / base::Time::kMicrosecondsPerDay
) *
363 base::Time::kMicrosecondsPerDay
);
365 // Local midnight could have been shifted due to time zone change.
366 base::Time then_midnight
=
367 base::Time::FromInternalValue(then_internal
).LocalMidnight();
368 base::Time midnight
= now
.LocalMidnight();
371 int days_since_last_update
= (midnight
- then_midnight
).InDays();
373 // Each day, we calculate the total number of bytes received and the total
374 // size of all corresponding resources before any data-reducing recompression
375 // is applied. These values are used to compute the data savings realized
376 // by applying our compression techniques. Totals for the last
377 // |kNumDaysInHistory| days are maintained.
378 DailyDataSavingUpdate
total(
379 data_reduction_proxy::prefs::kDailyHttpOriginalContentLength
,
380 data_reduction_proxy::prefs::kDailyHttpReceivedContentLength
,
382 total
.UpdateForDataChange(days_since_last_update
);
384 DailyDataSavingUpdate
proxy_enabled(
385 data_reduction_proxy::prefs::
386 kDailyOriginalContentLengthWithDataReductionProxyEnabled
,
387 data_reduction_proxy::prefs::
388 kDailyContentLengthWithDataReductionProxyEnabled
,
390 proxy_enabled
.UpdateForDataChange(days_since_last_update
);
392 DailyDataSavingUpdate
via_proxy(
393 data_reduction_proxy::prefs::
394 kDailyOriginalContentLengthViaDataReductionProxy
,
395 data_reduction_proxy::prefs::
396 kDailyContentLengthViaDataReductionProxy
,
398 via_proxy
.UpdateForDataChange(days_since_last_update
);
400 DailyContentLengthUpdate
https(
401 data_reduction_proxy::prefs::
402 kDailyContentLengthHttpsWithDataReductionProxyEnabled
,
404 https
.UpdateForDataChange(days_since_last_update
);
406 DailyContentLengthUpdate
short_bypass(
407 data_reduction_proxy::prefs::
408 kDailyContentLengthShortBypassWithDataReductionProxyEnabled
,
410 short_bypass
.UpdateForDataChange(days_since_last_update
);
412 DailyContentLengthUpdate
long_bypass(
413 data_reduction_proxy::prefs::
414 kDailyContentLengthLongBypassWithDataReductionProxyEnabled
,
416 long_bypass
.UpdateForDataChange(days_since_last_update
);
418 DailyContentLengthUpdate
unknown(
419 data_reduction_proxy::prefs::
420 kDailyContentLengthUnknownWithDataReductionProxyEnabled
,
422 unknown
.UpdateForDataChange(days_since_last_update
);
424 total
.Add(original_content_length
, received_content_length
);
425 if (with_data_reduction_proxy_enabled
) {
426 proxy_enabled
.Add(original_content_length
, received_content_length
);
427 // Ignore data source cases, if exist, when
428 // "with_data_reduction_proxy_enabled == false"
429 switch (request_type
) {
430 case VIA_DATA_REDUCTION_PROXY
:
431 via_proxy
.Add(original_content_length
, received_content_length
);
434 https
.Add(received_content_length
);
437 short_bypass
.Add(received_content_length
);
440 long_bypass
.Add(received_content_length
);
443 unknown
.Add(received_content_length
);
448 if (days_since_last_update
) {
449 // Record the last update time in microseconds in UTC.
451 data_reduction_proxy::prefs::kDailyHttpContentLengthLastUpdateDate
,
452 midnight
.ToInternalValue());
454 // A new day. Report the previous day's data if exists. We'll lose usage
455 // data if the last time Chrome was run was more than a day ago.
456 // Here, we prefer collecting less data but the collected data is
457 // associated with an accurate date.
458 if (days_since_last_update
== 1) {
459 // The previous day's data point is the second one from the tail.
460 // Therefore (kNumDaysInHistory - 2) below.
461 RecordDailyContentLengthHistograms(
462 total
.GetOriginalListPrefValue(kNumDaysInHistory
- 2),
463 total
.GetReceivedListPrefValue(kNumDaysInHistory
- 2),
464 proxy_enabled
.GetOriginalListPrefValue(kNumDaysInHistory
- 2),
465 proxy_enabled
.GetReceivedListPrefValue(kNumDaysInHistory
- 2),
466 via_proxy
.GetOriginalListPrefValue(kNumDaysInHistory
- 2),
467 via_proxy
.GetReceivedListPrefValue(kNumDaysInHistory
- 2),
468 https
.GetListPrefValue(kNumDaysInHistory
- 2),
469 short_bypass
.GetListPrefValue(kNumDaysInHistory
- 2),
470 long_bypass
.GetListPrefValue(kNumDaysInHistory
- 2),
471 unknown
.GetListPrefValue(kNumDaysInHistory
- 2));
476 void UpdateContentLengthPrefs(
477 int received_content_length
,
478 int original_content_length
,
479 bool with_data_reduction_proxy_enabled
,
480 DataReductionProxyRequestType request_type
,
481 PrefService
* prefs
) {
482 int64 total_received
= prefs
->GetInt64(
483 data_reduction_proxy::prefs::kHttpReceivedContentLength
);
484 int64 total_original
= prefs
->GetInt64(
485 data_reduction_proxy::prefs::kHttpOriginalContentLength
);
486 total_received
+= received_content_length
;
487 total_original
+= original_content_length
;
488 prefs
->SetInt64(data_reduction_proxy::prefs::kHttpReceivedContentLength
,
490 prefs
->SetInt64(data_reduction_proxy::prefs::kHttpOriginalContentLength
,
493 UpdateContentLengthPrefsForDataReductionProxy(
494 received_content_length
,
495 original_content_length
,
496 with_data_reduction_proxy_enabled
,
502 } // namespace data_reduction_proxy