Home | History | Annotate | Download | only in prerender
      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
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/prerender/prerender_histograms.h"
      6 
      7 #include <string>
      8 
      9 #include "base/format_macros.h"
     10 #include "base/metrics/histogram.h"
     11 #include "base/strings/stringprintf.h"
     12 #include "chrome/browser/predictors/autocomplete_action_predictor.h"
     13 #include "chrome/browser/prerender/prerender_manager.h"
     14 #include "chrome/browser/prerender/prerender_util.h"
     15 
     16 using predictors::AutocompleteActionPredictor;
     17 
     18 namespace prerender {
     19 
     20 namespace {
     21 
     22 // Time window for which we will record windowed PLTs from the last observed
     23 // link rel=prefetch tag. This is not intended to be the same as the prerender
     24 // ttl, it's just intended to be a window during which a prerender has likely
     25 // affected performance.
     26 const int kWindowDurationSeconds = 30;
     27 
     28 std::string ComposeHistogramName(const std::string& prefix_type,
     29                                  const std::string& name) {
     30   if (prefix_type.empty())
     31     return std::string("Prerender.") + name;
     32   return std::string("Prerender.") + prefix_type + std::string("_") + name;
     33 }
     34 
     35 std::string GetHistogramName(Origin origin, uint8 experiment_id,
     36                              bool is_wash, const std::string& name) {
     37   if (is_wash)
     38     return ComposeHistogramName("wash", name);
     39 
     40   if (origin == ORIGIN_GWS_PRERENDER) {
     41     if (experiment_id == kNoExperiment)
     42       return ComposeHistogramName("gws", name);
     43     return ComposeHistogramName("exp" + std::string(1, experiment_id + '0'),
     44                                 name);
     45   }
     46 
     47   if (experiment_id != kNoExperiment)
     48     return ComposeHistogramName("wash", name);
     49 
     50   switch (origin) {
     51     case ORIGIN_OMNIBOX:
     52       return ComposeHistogramName("omnibox", name);
     53     case ORIGIN_NONE:
     54       return ComposeHistogramName("none", name);
     55     case ORIGIN_LINK_REL_PRERENDER_SAMEDOMAIN:
     56       return ComposeHistogramName("websame", name);
     57     case ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN:
     58       return ComposeHistogramName("webcross", name);
     59     case ORIGIN_LOCAL_PREDICTOR:
     60       return ComposeHistogramName("localpredictor", name);
     61     case ORIGIN_GWS_PRERENDER:  // Handled above.
     62     default:
     63       NOTREACHED();
     64       break;
     65   };
     66 
     67   // Dummy return value to make the compiler happy.
     68   NOTREACHED();
     69   return ComposeHistogramName("wash", name);
     70 }
     71 
     72 bool OriginIsOmnibox(Origin origin) {
     73   return origin == ORIGIN_OMNIBOX;
     74 }
     75 
     76 }  // namespace
     77 
     78 // Helper macros for experiment-based and origin-based histogram reporting.
     79 // All HISTOGRAM arguments must be UMA_HISTOGRAM... macros that contain an
     80 // argument "name" which these macros will eventually substitute for the
     81 // actual name used.
     82 #define PREFIXED_HISTOGRAM(histogram_name, origin, HISTOGRAM)           \
     83   PREFIXED_HISTOGRAM_INTERNAL(origin, GetCurrentExperimentId(),         \
     84                               IsOriginExperimentWash(), HISTOGRAM, \
     85                               histogram_name)
     86 
     87 #define PREFIXED_HISTOGRAM_ORIGIN_EXPERIMENT(histogram_name, origin, \
     88                                              experiment, HISTOGRAM) \
     89   PREFIXED_HISTOGRAM_INTERNAL(origin, experiment, false, HISTOGRAM, \
     90                               histogram_name)
     91 
     92 #define PREFIXED_HISTOGRAM_INTERNAL(origin, experiment, wash, HISTOGRAM, \
     93                                     histogram_name) { \
     94   { \
     95     /* Do not rename.  HISTOGRAM expects a local variable "name". */           \
     96     std::string name = ComposeHistogramName(std::string(), histogram_name);    \
     97     HISTOGRAM;                                                                 \
     98   } \
     99   /* Do not rename.  HISTOGRAM expects a local variable "name". */ \
    100   std::string name = GetHistogramName(origin, experiment, wash, \
    101                                       histogram_name); \
    102   static uint8 recording_experiment = kNoExperiment; \
    103   if (recording_experiment == kNoExperiment && experiment != kNoExperiment) \
    104     recording_experiment = experiment; \
    105   if (wash) { \
    106     HISTOGRAM; \
    107   } else if (experiment != kNoExperiment && \
    108              (origin != ORIGIN_GWS_PRERENDER || \
    109               experiment != recording_experiment)) { \
    110   } else if (origin == ORIGIN_OMNIBOX) { \
    111     HISTOGRAM; \
    112   } else if (origin == ORIGIN_NONE) { \
    113     HISTOGRAM; \
    114   } else if (origin == ORIGIN_LINK_REL_PRERENDER_SAMEDOMAIN) { \
    115     HISTOGRAM; \
    116   } else if (origin == ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN) { \
    117     HISTOGRAM; \
    118   } else if (origin == ORIGIN_LOCAL_PREDICTOR) { \
    119     HISTOGRAM; \
    120   } else if (experiment != kNoExperiment) { \
    121     HISTOGRAM; \
    122   } else { \
    123     HISTOGRAM; \
    124   } \
    125 }
    126 
    127 PrerenderHistograms::PrerenderHistograms()
    128     : last_experiment_id_(kNoExperiment),
    129       last_origin_(ORIGIN_MAX),
    130       origin_experiment_wash_(false),
    131       seen_any_pageload_(true),
    132       seen_pageload_started_after_prerender_(true) {
    133 }
    134 
    135 void PrerenderHistograms::RecordPrerender(Origin origin, const GURL& url) {
    136   // Check if we are doing an experiment.
    137   uint8 experiment = GetQueryStringBasedExperiment(url);
    138 
    139   // We need to update last_experiment_id_, last_origin_, and
    140   // origin_experiment_wash_.
    141   if (!WithinWindow()) {
    142     // If we are outside a window, this is a fresh start and we are fine,
    143     // and there is no mix.
    144     origin_experiment_wash_ = false;
    145   } else {
    146     // If we are inside the last window, there is a mish mash of origins
    147     // and experiments if either there was a mish mash before, or the current
    148     // experiment/origin does not match the previous one.
    149     if (experiment != last_experiment_id_ || origin != last_origin_)
    150       origin_experiment_wash_ = true;
    151   }
    152 
    153   last_origin_ = origin;
    154   last_experiment_id_ = experiment;
    155 
    156   // If we observe multiple tags within the 30 second window, we will still
    157   // reset the window to begin at the most recent occurrence, so that we will
    158   // always be in a window in the 30 seconds from each occurrence.
    159   last_prerender_seen_time_ = GetCurrentTimeTicks();
    160   seen_any_pageload_ = false;
    161   seen_pageload_started_after_prerender_ = false;
    162 }
    163 
    164 void PrerenderHistograms::RecordPrerenderStarted(Origin origin) const {
    165   if (OriginIsOmnibox(origin)) {
    166     UMA_HISTOGRAM_ENUMERATION(
    167         base::StringPrintf("Prerender.OmniboxPrerenderCount%s",
    168                            PrerenderManager::GetModeString()), 1, 2);
    169   }
    170 }
    171 
    172 void PrerenderHistograms::RecordConcurrency(size_t prerender_count) const {
    173   static const size_t kMaxRecordableConcurrency = 20;
    174   DCHECK_GE(kMaxRecordableConcurrency, Config().max_link_concurrency);
    175   UMA_HISTOGRAM_ENUMERATION(
    176       base::StringPrintf("Prerender.PrerenderCountOf%" PRIuS "Max",
    177                          kMaxRecordableConcurrency),
    178       prerender_count, kMaxRecordableConcurrency + 1);
    179 }
    180 
    181 void PrerenderHistograms::RecordUsedPrerender(Origin origin) const {
    182   if (OriginIsOmnibox(origin)) {
    183     UMA_HISTOGRAM_ENUMERATION(
    184         base::StringPrintf("Prerender.OmniboxNavigationsUsedPrerenderCount%s",
    185                            PrerenderManager::GetModeString()), 1, 2);
    186   }
    187 }
    188 
    189 void PrerenderHistograms::RecordTimeSinceLastRecentVisit(
    190     Origin origin,
    191     base::TimeDelta delta) const {
    192   PREFIXED_HISTOGRAM(
    193       "TimeSinceLastRecentVisit", origin,
    194       UMA_HISTOGRAM_TIMES(name, delta));
    195 }
    196 
    197 void PrerenderHistograms::RecordFractionPixelsFinalAtSwapin(
    198     Origin origin,
    199     double fraction) const {
    200   if (fraction < 0.0 || fraction > 1.0)
    201     return;
    202   int percentage = static_cast<int>(fraction * 100);
    203   if (percentage < 0 || percentage > 100)
    204     return;
    205   PREFIXED_HISTOGRAM("FractionPixelsFinalAtSwapin",
    206                      origin, UMA_HISTOGRAM_PERCENTAGE(name, percentage));
    207 }
    208 
    209 base::TimeTicks PrerenderHistograms::GetCurrentTimeTicks() const {
    210   return base::TimeTicks::Now();
    211 }
    212 
    213 // Helper macro for histograms.
    214 #define RECORD_PLT(tag, perceived_page_load_time) { \
    215   PREFIXED_HISTOGRAM( \
    216       tag, origin, \
    217       UMA_HISTOGRAM_CUSTOM_TIMES( \
    218         name, \
    219         perceived_page_load_time, \
    220         base::TimeDelta::FromMilliseconds(10), \
    221         base::TimeDelta::FromSeconds(60), \
    222         100)); \
    223 }
    224 
    225 // Summary of all histograms Perceived PLT histograms:
    226 // (all prefixed PerceivedPLT)
    227 // PerceivedPLT -- Perceived Pageloadtimes (PPLT) for all pages in the group.
    228 // ...Windowed -- PPLT for pages in the 30s after a prerender is created.
    229 // ...Matched -- A prerendered page that was swapped in.  In the NoUse
    230 // and Control group cases, while nothing ever gets swapped in, we do keep
    231 // track of what would be prerendered and would be swapped in -- and those
    232 // cases are what is classified as Match for these groups.
    233 // ...MatchedComplete -- A prerendered page that was swapped in + a few
    234 // that were not swapped in so that the set of pages lines up more closely with
    235 // the control group.
    236 // ...FirstAfterMiss -- First page to finish loading after a prerender, which
    237 // is different from the page that was prerendered.
    238 // ...FirstAfterMissNonOverlapping -- Same as FirstAfterMiss, but only
    239 // triggering for the first page to finish after the prerender that also started
    240 // after the prerender started.
    241 // ...FirstAfterMissBoth -- pages meeting
    242 // FirstAfterMiss AND FirstAfterMissNonOverlapping
    243 // ...FirstAfterMissAnyOnly -- pages meeting
    244 // FirstAfterMiss but NOT FirstAfterMissNonOverlapping
    245 // ..FirstAfterMissNonOverlappingOnly -- pages meeting
    246 // FirstAfterMissNonOverlapping but NOT FirstAfterMiss
    247 
    248 void PrerenderHistograms::RecordPerceivedPageLoadTime(
    249     Origin origin,
    250     base::TimeDelta perceived_page_load_time,
    251     bool was_prerender,
    252     bool was_complete_prerender, const GURL& url) {
    253   if (!IsWebURL(url))
    254     return;
    255   bool within_window = WithinWindow();
    256   bool is_google_url = IsGoogleDomain(url);
    257   RECORD_PLT("PerceivedPLT", perceived_page_load_time);
    258   if (within_window)
    259     RECORD_PLT("PerceivedPLTWindowed", perceived_page_load_time);
    260   if (was_prerender || was_complete_prerender) {
    261     if (was_prerender)
    262       RECORD_PLT("PerceivedPLTMatched", perceived_page_load_time);
    263     if (was_complete_prerender)
    264       RECORD_PLT("PerceivedPLTMatchedComplete", perceived_page_load_time);
    265     seen_any_pageload_ = true;
    266     seen_pageload_started_after_prerender_ = true;
    267   } else if (within_window) {
    268     RECORD_PLT("PerceivedPLTWindowNotMatched", perceived_page_load_time);
    269     if (!is_google_url) {
    270       bool recorded_any = false;
    271       bool recorded_non_overlapping = false;
    272       if (!seen_any_pageload_) {
    273         seen_any_pageload_ = true;
    274         RECORD_PLT("PerceivedPLTFirstAfterMiss", perceived_page_load_time);
    275         recorded_any = true;
    276       }
    277       if (!seen_pageload_started_after_prerender_ &&
    278           perceived_page_load_time <= GetTimeSinceLastPrerender()) {
    279         seen_pageload_started_after_prerender_ = true;
    280         RECORD_PLT("PerceivedPLTFirstAfterMissNonOverlapping",
    281                    perceived_page_load_time);
    282         recorded_non_overlapping = true;
    283       }
    284       if (recorded_any || recorded_non_overlapping) {
    285         if (recorded_any && recorded_non_overlapping) {
    286           RECORD_PLT("PerceivedPLTFirstAfterMissBoth",
    287                      perceived_page_load_time);
    288         } else if (recorded_any) {
    289           RECORD_PLT("PerceivedPLTFirstAfterMissAnyOnly",
    290                      perceived_page_load_time);
    291         } else if (recorded_non_overlapping) {
    292           RECORD_PLT("PerceivedPLTFirstAfterMissNonOverlappingOnly",
    293                      perceived_page_load_time);
    294         }
    295       }
    296     }
    297   }
    298 }
    299 
    300 void PrerenderHistograms::RecordPageLoadTimeNotSwappedIn(
    301     Origin origin,
    302     base::TimeDelta page_load_time,
    303     const GURL& url) const {
    304   // If the URL to be prerendered is not a http[s] URL, or is a Google URL,
    305   // do not record.
    306   if (!IsWebURL(url) || IsGoogleDomain(url))
    307     return;
    308   RECORD_PLT("PrerenderNotSwappedInPLT", page_load_time);
    309 }
    310 
    311 void PrerenderHistograms::RecordPercentLoadDoneAtSwapin(Origin origin,
    312                                                         double fraction) const {
    313   if (fraction < 0.0 || fraction > 1.0)
    314     return;
    315   int percentage = static_cast<int>(fraction * 100);
    316   if (percentage < 0 || percentage > 100)
    317     return;
    318   PREFIXED_HISTOGRAM("PercentLoadDoneAtSwapin",
    319                      origin, UMA_HISTOGRAM_PERCENTAGE(name, percentage));
    320 }
    321 
    322 base::TimeDelta PrerenderHistograms::GetTimeSinceLastPrerender() const {
    323   return base::TimeTicks::Now() - last_prerender_seen_time_;
    324 }
    325 
    326 bool PrerenderHistograms::WithinWindow() const {
    327   if (last_prerender_seen_time_.is_null())
    328     return false;
    329   return GetTimeSinceLastPrerender() <=
    330       base::TimeDelta::FromSeconds(kWindowDurationSeconds);
    331 }
    332 
    333 void PrerenderHistograms::RecordTimeUntilUsed(
    334     Origin origin,
    335     base::TimeDelta time_until_used) const {
    336   PREFIXED_HISTOGRAM(
    337       "TimeUntilUsed2", origin,
    338       UMA_HISTOGRAM_CUSTOM_TIMES(
    339           name,
    340           time_until_used,
    341           base::TimeDelta::FromMilliseconds(10),
    342           base::TimeDelta::FromMinutes(30),
    343           50));
    344 }
    345 
    346 void PrerenderHistograms::RecordPerSessionCount(Origin origin,
    347                                                 int count) const {
    348   PREFIXED_HISTOGRAM(
    349       "PrerendersPerSessionCount", origin,
    350       UMA_HISTOGRAM_COUNTS(name, count));
    351 }
    352 
    353 void PrerenderHistograms::RecordTimeBetweenPrerenderRequests(
    354     Origin origin, base::TimeDelta time) const {
    355   PREFIXED_HISTOGRAM(
    356       "TimeBetweenPrerenderRequests", origin,
    357       UMA_HISTOGRAM_TIMES(name, time));
    358 }
    359 
    360 void PrerenderHistograms::RecordFinalStatus(
    361     Origin origin,
    362     uint8 experiment_id,
    363     PrerenderContents::MatchCompleteStatus mc_status,
    364     FinalStatus final_status) const {
    365   DCHECK(final_status != FINAL_STATUS_MAX);
    366 
    367   if (mc_status == PrerenderContents::MATCH_COMPLETE_DEFAULT ||
    368       mc_status == PrerenderContents::MATCH_COMPLETE_REPLACED) {
    369     PREFIXED_HISTOGRAM_ORIGIN_EXPERIMENT(
    370         "FinalStatus", origin, experiment_id,
    371         UMA_HISTOGRAM_ENUMERATION(name, final_status, FINAL_STATUS_MAX));
    372   }
    373   if (mc_status == PrerenderContents::MATCH_COMPLETE_DEFAULT ||
    374       mc_status == PrerenderContents::MATCH_COMPLETE_REPLACEMENT ||
    375       mc_status == PrerenderContents::MATCH_COMPLETE_REPLACEMENT_PENDING) {
    376     PREFIXED_HISTOGRAM_ORIGIN_EXPERIMENT(
    377         "FinalStatusMatchComplete", origin, experiment_id,
    378         UMA_HISTOGRAM_ENUMERATION(name, final_status, FINAL_STATUS_MAX));
    379   }
    380 }
    381 
    382 uint8 PrerenderHistograms::GetCurrentExperimentId() const {
    383   if (!WithinWindow())
    384     return kNoExperiment;
    385   return last_experiment_id_;
    386 }
    387 
    388 bool PrerenderHistograms::IsOriginExperimentWash() const {
    389   if (!WithinWindow())
    390     return false;
    391   return origin_experiment_wash_;
    392 }
    393 
    394 }  // namespace prerender
    395