- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / prerender / prerender_histograms.cc
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 (!url.SchemeIsHTTPOrHTTPS())
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 (!url.SchemeIsHTTPOrHTTPS() || 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