Home | History | Annotate | Download | only in browser
      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.
      4 
      5 #include "components/data_reduction_proxy/browser/data_reduction_proxy_metrics.h"
      6 
      7 #include "base/metrics/histogram.h"
      8 #include "base/prefs/pref_service.h"
      9 #include "base/strings/string_number_conversions.h"
     10 #include "base/strings/string_util.h"
     11 #include "components/data_reduction_proxy/browser/data_reduction_proxy_settings.h"
     12 #include "components/data_reduction_proxy/browser/data_reduction_proxy_statistics_prefs.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"
     20 
     21 namespace data_reduction_proxy {
     22 
     23 namespace {
     24 
     25 // A bypass delay more than this is treated as a long delay.
     26 const int kLongBypassDelayInSeconds = 30 * 60;
     27 
     28 // Increments an int64, stored as a string, in a ListPref at the specified
     29 // index.  The value must already exist and be a string representation of a
     30 // number.
     31 void AddInt64ToListPref(size_t index,
     32                         int64 length,
     33                         base::ListValue* list_update) {
     34   int64 value = 0;
     35   std::string old_string_value;
     36   bool rv = list_update->GetString(index, &old_string_value);
     37   DCHECK(rv);
     38   if (rv) {
     39     rv = base::StringToInt64(old_string_value, &value);
     40     DCHECK(rv);
     41   }
     42   value += length;
     43   list_update->Set(index, new base::StringValue(base::Int64ToString(value)));
     44 }
     45 
     46 int64 ListPrefInt64Value(const base::ListValue& list_update, size_t index) {
     47   std::string string_value;
     48   if (!list_update.GetString(index, &string_value)) {
     49     NOTREACHED();
     50     return 0;
     51   }
     52 
     53   int64 value = 0;
     54   bool rv = base::StringToInt64(string_value, &value);
     55   DCHECK(rv);
     56   return value;
     57 }
     58 
     59 // Report UMA metrics for daily data reductions.
     60 void RecordDailyContentLengthHistograms(
     61     int64 original_length,
     62     int64 received_length,
     63     int64 original_length_with_data_reduction_enabled,
     64     int64 received_length_with_data_reduction_enabled,
     65     int64 original_length_via_data_reduction_proxy,
     66     int64 received_length_via_data_reduction_proxy,
     67     int64 https_length_with_data_reduction_enabled,
     68     int64 short_bypass_length_with_data_reduction_enabled,
     69     int64 long_bypass_length_with_data_reduction_enabled,
     70     int64 unknown_length_with_data_reduction_enabled) {
     71   // Report daily UMA only for days having received content.
     72   if (original_length <= 0 || received_length <= 0)
     73     return;
     74 
     75   // Record metrics in KB.
     76   UMA_HISTOGRAM_COUNTS(
     77       "Net.DailyOriginalContentLength", original_length >> 10);
     78   UMA_HISTOGRAM_COUNTS(
     79       "Net.DailyContentLength", received_length >> 10);
     80   int percent = 0;
     81   // UMA percentage cannot be negative.
     82   if (original_length > received_length) {
     83     percent = (100 * (original_length - received_length)) / original_length;
     84   }
     85   UMA_HISTOGRAM_PERCENTAGE("Net.DailyContentSavingPercent", percent);
     86 
     87   if (original_length_with_data_reduction_enabled <= 0 ||
     88       received_length_with_data_reduction_enabled <= 0) {
     89     return;
     90   }
     91 
     92   UMA_HISTOGRAM_COUNTS(
     93       "Net.DailyOriginalContentLength_DataReductionProxyEnabled",
     94       original_length_with_data_reduction_enabled >> 10);
     95   UMA_HISTOGRAM_COUNTS(
     96       "Net.DailyContentLength_DataReductionProxyEnabled",
     97       received_length_with_data_reduction_enabled >> 10);
     98 
     99   int percent_data_reduction_proxy_enabled = 0;
    100   // UMA percentage cannot be negative.
    101   if (original_length_with_data_reduction_enabled >
    102       received_length_with_data_reduction_enabled) {
    103     percent_data_reduction_proxy_enabled =
    104         100 * (original_length_with_data_reduction_enabled -
    105                received_length_with_data_reduction_enabled) /
    106         original_length_with_data_reduction_enabled;
    107   }
    108   UMA_HISTOGRAM_PERCENTAGE(
    109       "Net.DailyContentSavingPercent_DataReductionProxyEnabled",
    110       percent_data_reduction_proxy_enabled);
    111 
    112   UMA_HISTOGRAM_PERCENTAGE(
    113       "Net.DailyContentPercent_DataReductionProxyEnabled",
    114       (100 * received_length_with_data_reduction_enabled) / received_length);
    115 
    116   DCHECK_GE(https_length_with_data_reduction_enabled, 0);
    117   UMA_HISTOGRAM_COUNTS(
    118       "Net.DailyContentLength_DataReductionProxyEnabled_Https",
    119       https_length_with_data_reduction_enabled >> 10);
    120   UMA_HISTOGRAM_PERCENTAGE(
    121       "Net.DailyContentPercent_DataReductionProxyEnabled_Https",
    122       (100 * https_length_with_data_reduction_enabled) / received_length);
    123 
    124   DCHECK_GE(short_bypass_length_with_data_reduction_enabled, 0);
    125   UMA_HISTOGRAM_COUNTS(
    126       "Net.DailyContentLength_DataReductionProxyEnabled_ShortBypass",
    127       short_bypass_length_with_data_reduction_enabled >> 10);
    128   UMA_HISTOGRAM_PERCENTAGE(
    129       "Net.DailyContentPercent_DataReductionProxyEnabled_ShortBypass",
    130       ((100 * short_bypass_length_with_data_reduction_enabled) /
    131        received_length));
    132 
    133   DCHECK_GE(long_bypass_length_with_data_reduction_enabled, 0);
    134   UMA_HISTOGRAM_COUNTS(
    135       "Net.DailyContentLength_DataReductionProxyEnabled_LongBypass",
    136       long_bypass_length_with_data_reduction_enabled >> 10);
    137   UMA_HISTOGRAM_PERCENTAGE(
    138       "Net.DailyContentPercent_DataReductionProxyEnabled_LongBypass",
    139       ((100 * long_bypass_length_with_data_reduction_enabled) /
    140        received_length));
    141 
    142   DCHECK_GE(unknown_length_with_data_reduction_enabled, 0);
    143   UMA_HISTOGRAM_COUNTS(
    144       "Net.DailyContentLength_DataReductionProxyEnabled_Unknown",
    145       unknown_length_with_data_reduction_enabled >> 10);
    146   UMA_HISTOGRAM_PERCENTAGE(
    147       "Net.DailyContentPercent_DataReductionProxyEnabled_Unknown",
    148       ((100 * unknown_length_with_data_reduction_enabled) /
    149        received_length));
    150 
    151   DCHECK_GE(original_length_via_data_reduction_proxy, 0);
    152   UMA_HISTOGRAM_COUNTS(
    153       "Net.DailyOriginalContentLength_ViaDataReductionProxy",
    154       original_length_via_data_reduction_proxy >> 10);
    155   DCHECK_GE(received_length_via_data_reduction_proxy, 0);
    156   UMA_HISTOGRAM_COUNTS(
    157       "Net.DailyContentLength_ViaDataReductionProxy",
    158       received_length_via_data_reduction_proxy >> 10);
    159   int percent_via_data_reduction_proxy = 0;
    160   if (original_length_via_data_reduction_proxy >
    161       received_length_via_data_reduction_proxy) {
    162     percent_via_data_reduction_proxy =
    163         100 * (original_length_via_data_reduction_proxy -
    164                received_length_via_data_reduction_proxy) /
    165         original_length_via_data_reduction_proxy;
    166   }
    167   UMA_HISTOGRAM_PERCENTAGE(
    168       "Net.DailyContentSavingPercent_ViaDataReductionProxy",
    169       percent_via_data_reduction_proxy);
    170   UMA_HISTOGRAM_PERCENTAGE(
    171       "Net.DailyContentPercent_ViaDataReductionProxy",
    172       (100 * received_length_via_data_reduction_proxy) / received_length);
    173 }
    174 
    175 // Ensure list has exactly |length| elements, either by truncating at the
    176 // front, or appending "0"'s to the back.
    177 void MaintainContentLengthPrefsWindow(base::ListValue* list, size_t length) {
    178   // Remove data for old days from the front.
    179   while (list->GetSize() > length)
    180     list->Remove(0, NULL);
    181   // Newly added lists are empty. Add entries to back to fill the window,
    182   // each initialized to zero.
    183   while (list->GetSize() < length)
    184     list->AppendString(base::Int64ToString(0));
    185   DCHECK_EQ(length, list->GetSize());
    186 }
    187 
    188 // DailyContentLengthUpdate maintains a data saving pref. The pref is a list
    189 // of |kNumDaysInHistory| elements of daily total content lengths for the past
    190 // |kNumDaysInHistory| days.
    191 class DailyContentLengthUpdate {
    192  public:
    193   DailyContentLengthUpdate(const char* pref,
    194                            DataReductionProxyStatisticsPrefs* pref_service)
    195       : update_(pref_service->GetList(pref)) {
    196   }
    197 
    198   void UpdateForDataChange(int days_since_last_update) {
    199     // New empty lists may have been created. Maintain the invariant that
    200     // there should be exactly |kNumDaysInHistory| days in the histories.
    201     MaintainContentLengthPrefsWindow(update_, kNumDaysInHistory);
    202     if (days_since_last_update) {
    203       MaintainContentLengthPrefForDateChange(days_since_last_update);
    204     }
    205   }
    206 
    207   // Update the lengths for the current day.
    208   void Add(int content_length) {
    209     AddInt64ToListPref(kNumDaysInHistory - 1, content_length, update_);
    210   }
    211 
    212   int64 GetListPrefValue(size_t index) {
    213     return ListPrefInt64Value(*update_, index);
    214   }
    215 
    216  private:
    217   // Update the list for date change and ensure the list has exactly |length|
    218   // elements. The last entry in the list will be for the current day after
    219   // the update.
    220   void MaintainContentLengthPrefForDateChange(int days_since_last_update) {
    221     if (days_since_last_update == -1) {
    222       // The system may go backwards in time by up to a day for legitimate
    223       // reasons, such as with changes to the time zone. In such cases, we
    224       // keep adding to the current day.
    225       // Note: we accept the fact that some reported data is shifted to
    226       // the adjacent day if users travel back and forth across time zones.
    227       days_since_last_update = 0;
    228     } else if (days_since_last_update < -1) {
    229       // Erase all entries if the system went backwards in time by more than
    230       // a day.
    231       update_->Clear();
    232 
    233       days_since_last_update = kNumDaysInHistory;
    234     }
    235     DCHECK_GE(days_since_last_update, 0);
    236 
    237     // Add entries for days since last update event. This will make the
    238     // lists longer than kNumDaysInHistory. The additional items will be cut off
    239     // from the head of the lists by |MaintainContentLengthPrefsWindow|, below.
    240     for (int i = 0;
    241          i < days_since_last_update && i < static_cast<int>(kNumDaysInHistory);
    242          ++i) {
    243       update_->AppendString(base::Int64ToString(0));
    244     }
    245 
    246     // Entries for new days may have been appended. Maintain the invariant that
    247     // there should be exactly |kNumDaysInHistory| days in the histories.
    248     MaintainContentLengthPrefsWindow(update_, kNumDaysInHistory);
    249   }
    250 
    251   base::ListValue* update_;
    252 };
    253 
    254 // DailyDataSavingUpdate maintains a pair of data saving prefs, original_update_
    255 // and received_update_. pref_original is a list of |kNumDaysInHistory| elements
    256 // of daily total original content lengths for the past |kNumDaysInHistory|
    257 // days. pref_received is the corresponding list of the daily total received
    258 // content lengths.
    259 class DailyDataSavingUpdate {
    260  public:
    261   DailyDataSavingUpdate(const char* pref_original,
    262                         const char* pref_received,
    263                         DataReductionProxyStatisticsPrefs* prefs)
    264       : original_(pref_original, prefs),
    265         received_(pref_received, prefs) {
    266   }
    267 
    268   void UpdateForDataChange(int days_since_last_update) {
    269     original_.UpdateForDataChange(days_since_last_update);
    270     received_.UpdateForDataChange(days_since_last_update);
    271   }
    272 
    273   // Update the lengths for the current day.
    274   void Add(int original_content_length, int received_content_length) {
    275     original_.Add(original_content_length);
    276     received_.Add(received_content_length);
    277   }
    278 
    279   int64 GetOriginalListPrefValue(size_t index) {
    280     return original_.GetListPrefValue(index);
    281   }
    282   int64 GetReceivedListPrefValue(size_t index) {
    283     return received_.GetListPrefValue(index);
    284   }
    285 
    286  private:
    287   DailyContentLengthUpdate original_;
    288   DailyContentLengthUpdate received_;
    289 };
    290 
    291 }  // namespace
    292 
    293 DataReductionProxyRequestType GetDataReductionProxyRequestType(
    294     const net::URLRequest* request) {
    295   if (request->url().SchemeIs("https"))
    296     return HTTPS;
    297   if (!request->url().SchemeIs("http")) {
    298     NOTREACHED();
    299     return UNKNOWN_TYPE;
    300   }
    301   DataReductionProxyParams params(
    302         DataReductionProxyParams::kAllowed |
    303         DataReductionProxyParams::kFallbackAllowed |
    304         DataReductionProxyParams::kPromoAllowed);
    305   base::TimeDelta bypass_delay;
    306   if (params.AreDataReductionProxiesBypassed(*request, &bypass_delay)) {
    307     if (bypass_delay > base::TimeDelta::FromSeconds(kLongBypassDelayInSeconds))
    308       return LONG_BYPASS;
    309     return SHORT_BYPASS;
    310   }
    311   if (request->response_info().headers.get() &&
    312       HasDataReductionProxyViaHeader(request->response_info().headers.get(),
    313                                      NULL)) {
    314     return VIA_DATA_REDUCTION_PROXY;
    315   }
    316   return UNKNOWN_TYPE;
    317 }
    318 
    319 int64 GetAdjustedOriginalContentLength(
    320     DataReductionProxyRequestType request_type,
    321     int64 original_content_length,
    322     int64 received_content_length) {
    323   // Since there was no indication of the original content length, presume
    324   // it is no different from the number of bytes read.
    325   if (original_content_length == -1 ||
    326       request_type != VIA_DATA_REDUCTION_PROXY) {
    327     return received_content_length;
    328   }
    329   return original_content_length;
    330 }
    331 
    332 void UpdateContentLengthPrefsForDataReductionProxy(
    333     int received_content_length,
    334     int original_content_length,
    335     bool with_data_reduction_proxy_enabled,
    336     DataReductionProxyRequestType request_type,
    337     base::Time now,
    338     DataReductionProxyStatisticsPrefs* prefs) {
    339   // TODO(bengr): Remove this check once the underlying cause of
    340   // http://crbug.com/287821 is fixed. For now, only continue if the current
    341   // year is reported as being between 1972 and 2970.
    342   base::TimeDelta time_since_unix_epoch = now - base::Time::UnixEpoch();
    343   const int kMinDaysSinceUnixEpoch = 365 * 2;  // 2 years.
    344   const int kMaxDaysSinceUnixEpoch = 365 * 1000;  // 1000 years.
    345   if (time_since_unix_epoch.InDays() < kMinDaysSinceUnixEpoch ||
    346       time_since_unix_epoch.InDays() > kMaxDaysSinceUnixEpoch) {
    347     return;
    348   }
    349 
    350   // Determine how many days it has been since the last update.
    351   int64 then_internal = prefs->GetInt64(
    352       data_reduction_proxy::prefs::kDailyHttpContentLengthLastUpdateDate);
    353 
    354   // Local midnight could have been shifted due to time zone change.
    355   // If time is null then don't care if midnight will be wrong shifted due to
    356   // time zone change because it's still too much time ago.
    357   base::Time then_midnight = base::Time::FromInternalValue(then_internal);
    358   if (!then_midnight.is_null()) {
    359     then_midnight = then_midnight.LocalMidnight();
    360   }
    361   base::Time midnight = now.LocalMidnight();
    362 
    363   int days_since_last_update = (midnight - then_midnight).InDays();
    364 
    365   // Each day, we calculate the total number of bytes received and the total
    366   // size of all corresponding resources before any data-reducing recompression
    367   // is applied. These values are used to compute the data savings realized
    368   // by applying our compression techniques. Totals for the last
    369   // |kNumDaysInHistory| days are maintained.
    370   DailyDataSavingUpdate total(
    371       data_reduction_proxy::prefs::kDailyHttpOriginalContentLength,
    372       data_reduction_proxy::prefs::kDailyHttpReceivedContentLength,
    373       prefs);
    374   total.UpdateForDataChange(days_since_last_update);
    375 
    376   DailyDataSavingUpdate proxy_enabled(
    377       data_reduction_proxy::prefs::
    378           kDailyOriginalContentLengthWithDataReductionProxyEnabled,
    379       data_reduction_proxy::prefs::
    380           kDailyContentLengthWithDataReductionProxyEnabled,
    381       prefs);
    382   proxy_enabled.UpdateForDataChange(days_since_last_update);
    383 
    384   DailyDataSavingUpdate via_proxy(
    385       data_reduction_proxy::prefs::
    386           kDailyOriginalContentLengthViaDataReductionProxy,
    387       data_reduction_proxy::prefs::
    388           kDailyContentLengthViaDataReductionProxy,
    389       prefs);
    390   via_proxy.UpdateForDataChange(days_since_last_update);
    391 
    392   DailyContentLengthUpdate https(
    393       data_reduction_proxy::prefs::
    394           kDailyContentLengthHttpsWithDataReductionProxyEnabled,
    395       prefs);
    396   https.UpdateForDataChange(days_since_last_update);
    397 
    398   DailyContentLengthUpdate short_bypass(
    399       data_reduction_proxy::prefs::
    400           kDailyContentLengthShortBypassWithDataReductionProxyEnabled,
    401       prefs);
    402   short_bypass.UpdateForDataChange(days_since_last_update);
    403 
    404   DailyContentLengthUpdate long_bypass(
    405       data_reduction_proxy::prefs::
    406           kDailyContentLengthLongBypassWithDataReductionProxyEnabled,
    407       prefs);
    408   long_bypass.UpdateForDataChange(days_since_last_update);
    409 
    410   DailyContentLengthUpdate unknown(
    411       data_reduction_proxy::prefs::
    412           kDailyContentLengthUnknownWithDataReductionProxyEnabled,
    413       prefs);
    414   unknown.UpdateForDataChange(days_since_last_update);
    415 
    416   total.Add(original_content_length, received_content_length);
    417   if (with_data_reduction_proxy_enabled) {
    418     proxy_enabled.Add(original_content_length, received_content_length);
    419     // Ignore data source cases, if exist, when
    420     // "with_data_reduction_proxy_enabled == false"
    421     switch (request_type) {
    422       case VIA_DATA_REDUCTION_PROXY:
    423         via_proxy.Add(original_content_length, received_content_length);
    424         break;
    425       case HTTPS:
    426         https.Add(received_content_length);
    427         break;
    428       case SHORT_BYPASS:
    429         short_bypass.Add(received_content_length);
    430         break;
    431       case LONG_BYPASS:
    432         long_bypass.Add(received_content_length);
    433         break;
    434       case UNKNOWN_TYPE:
    435         unknown.Add(received_content_length);
    436         break;
    437     }
    438   }
    439 
    440   if (days_since_last_update) {
    441     // Record the last update time in microseconds in UTC.
    442     prefs->SetInt64(
    443         data_reduction_proxy::prefs::kDailyHttpContentLengthLastUpdateDate,
    444         midnight.ToInternalValue());
    445 
    446     // A new day. Report the previous day's data if exists. We'll lose usage
    447     // data if the last time Chrome was run was more than a day ago.
    448     // Here, we prefer collecting less data but the collected data is
    449     // associated with an accurate date.
    450     if (days_since_last_update == 1) {
    451       // The previous day's data point is the second one from the tail.
    452       // Therefore (kNumDaysInHistory - 2) below.
    453       RecordDailyContentLengthHistograms(
    454           total.GetOriginalListPrefValue(kNumDaysInHistory - 2),
    455           total.GetReceivedListPrefValue(kNumDaysInHistory - 2),
    456           proxy_enabled.GetOriginalListPrefValue(kNumDaysInHistory - 2),
    457           proxy_enabled.GetReceivedListPrefValue(kNumDaysInHistory - 2),
    458           via_proxy.GetOriginalListPrefValue(kNumDaysInHistory - 2),
    459           via_proxy.GetReceivedListPrefValue(kNumDaysInHistory - 2),
    460           https.GetListPrefValue(kNumDaysInHistory - 2),
    461           short_bypass.GetListPrefValue(kNumDaysInHistory - 2),
    462           long_bypass.GetListPrefValue(kNumDaysInHistory - 2),
    463           unknown.GetListPrefValue(kNumDaysInHistory - 2));
    464     }
    465   }
    466 }
    467 
    468 void UpdateContentLengthPrefs(int received_content_length,
    469                               int original_content_length,
    470                               PrefService* profile_prefs,
    471                               DataReductionProxyRequestType request_type,
    472                               DataReductionProxyStatisticsPrefs* prefs) {
    473   int64 total_received = prefs->GetInt64(
    474       data_reduction_proxy::prefs::kHttpReceivedContentLength);
    475   int64 total_original = prefs->GetInt64(
    476       data_reduction_proxy::prefs::kHttpOriginalContentLength);
    477   total_received += received_content_length;
    478   total_original += original_content_length;
    479   prefs->SetInt64(
    480       data_reduction_proxy::prefs::kHttpReceivedContentLength,
    481       total_received);
    482   prefs->SetInt64(
    483       data_reduction_proxy::prefs::kHttpOriginalContentLength,
    484       total_original);
    485 
    486   bool with_data_reduction_proxy_enabled =
    487       profile_prefs->GetBoolean(
    488           data_reduction_proxy::prefs::kDataReductionProxyEnabled);
    489   UpdateContentLengthPrefsForDataReductionProxy(
    490       received_content_length,
    491       original_content_length,
    492       with_data_reduction_proxy_enabled,
    493       request_type,
    494       base::Time::Now(),
    495       prefs);
    496 }
    497 
    498 }  // namespace data_reduction_proxy
    499