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.
5 #include "chrome/browser/prerender/prerender_histograms.h"
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"
16 using predictors::AutocompleteActionPredictor
;
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;
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
;
35 std::string
GetHistogramName(Origin origin
, bool is_wash
,
36 const std::string
& name
) {
38 return ComposeHistogramName("wash", name
);
42 return ComposeHistogramName("omnibox", name
);
44 return ComposeHistogramName("none", name
);
45 case ORIGIN_LINK_REL_PRERENDER_SAMEDOMAIN
:
46 return ComposeHistogramName("websame", name
);
47 case ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN
:
48 return ComposeHistogramName("webcross", name
);
49 case ORIGIN_EXTERNAL_REQUEST
:
50 return ComposeHistogramName("externalrequest", name
);
52 return ComposeHistogramName("Instant", name
);
53 case ORIGIN_LINK_REL_NEXT
:
54 return ComposeHistogramName("webnext", name
);
55 case ORIGIN_GWS_PRERENDER
:
56 return ComposeHistogramName("gws", name
);
62 // Dummy return value to make the compiler happy.
64 return ComposeHistogramName("wash", name
);
67 bool OriginIsOmnibox(Origin origin
) {
68 return origin
== ORIGIN_OMNIBOX
;
73 // Helper macros for origin-based histogram reporting. All HISTOGRAM arguments
74 // must be UMA_HISTOGRAM... macros that contain an argument "name" which these
75 // macros will eventually substitute for the actual name used.
76 #define PREFIXED_HISTOGRAM(histogram_name, origin, HISTOGRAM) \
77 PREFIXED_HISTOGRAM_INTERNAL(origin, IsOriginWash(), HISTOGRAM, histogram_name)
79 #define PREFIXED_HISTOGRAM_ORIGIN_EXPERIMENT(histogram_name, origin, \
81 PREFIXED_HISTOGRAM_INTERNAL(origin, false, HISTOGRAM, histogram_name)
83 #define PREFIXED_HISTOGRAM_INTERNAL(origin, wash, HISTOGRAM, histogram_name) \
86 /* Do not rename. HISTOGRAM expects a local variable "name". */ \
87 std::string name = ComposeHistogramName(std::string(), histogram_name); \
90 /* Do not rename. HISTOGRAM expects a local variable "name". */ \
91 std::string name = GetHistogramName(origin, wash, histogram_name); \
94 } else if (origin == ORIGIN_OMNIBOX) { \
96 } else if (origin == ORIGIN_NONE) { \
98 } else if (origin == ORIGIN_LINK_REL_PRERENDER_SAMEDOMAIN) { \
100 } else if (origin == ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN) { \
102 } else if (origin == ORIGIN_EXTERNAL_REQUEST) { \
104 } else if (origin == ORIGIN_INSTANT) { \
106 } else if (origin == ORIGIN_LINK_REL_NEXT) { \
113 PrerenderHistograms::PrerenderHistograms()
114 : last_origin_(ORIGIN_MAX
),
116 seen_any_pageload_(true),
117 seen_pageload_started_after_prerender_(true) {
120 void PrerenderHistograms::RecordPrerender(Origin origin
, const GURL
& url
) {
121 // We need to update last_origin_ and origin_wash_.
122 if (!WithinWindow()) {
123 // If we are outside a window, this is a fresh start and we are fine,
124 // and there is no mix.
125 origin_wash_
= false;
127 // If we are inside the last window, there is a mish mash of origins if
128 // either there was a mish mash before, or the current origin does not match
130 if (origin
!= last_origin_
)
134 last_origin_
= origin
;
136 // If we observe multiple tags within the 30 second window, we will still
137 // reset the window to begin at the most recent occurrence, so that we will
138 // always be in a window in the 30 seconds from each occurrence.
139 last_prerender_seen_time_
= GetCurrentTimeTicks();
140 seen_any_pageload_
= false;
141 seen_pageload_started_after_prerender_
= false;
144 void PrerenderHistograms::RecordPrerenderStarted(Origin origin
) const {
145 if (OriginIsOmnibox(origin
)) {
146 UMA_HISTOGRAM_ENUMERATION(
147 base::StringPrintf("Prerender.OmniboxPrerenderCount%s",
148 PrerenderManager::GetModeString()), 1, 2);
152 void PrerenderHistograms::RecordConcurrency(size_t prerender_count
) const {
153 static const size_t kMaxRecordableConcurrency
= 20;
154 DCHECK_GE(kMaxRecordableConcurrency
, Config().max_link_concurrency
);
155 UMA_HISTOGRAM_ENUMERATION(
156 base::StringPrintf("Prerender.PrerenderCountOf%" PRIuS
"Max",
157 kMaxRecordableConcurrency
),
158 prerender_count
, kMaxRecordableConcurrency
+ 1);
161 void PrerenderHistograms::RecordUsedPrerender(Origin origin
) const {
162 if (OriginIsOmnibox(origin
)) {
163 UMA_HISTOGRAM_ENUMERATION(
164 base::StringPrintf("Prerender.OmniboxNavigationsUsedPrerenderCount%s",
165 PrerenderManager::GetModeString()), 1, 2);
169 void PrerenderHistograms::RecordTimeSinceLastRecentVisit(
171 base::TimeDelta delta
) const {
173 "TimeSinceLastRecentVisit", origin
,
174 UMA_HISTOGRAM_TIMES(name
, delta
));
177 base::TimeTicks
PrerenderHistograms::GetCurrentTimeTicks() const {
178 return base::TimeTicks::Now();
181 // Helper macro for histograms.
182 #define RECORD_PLT(tag, perceived_page_load_time) \
183 PREFIXED_HISTOGRAM( \
185 UMA_HISTOGRAM_CUSTOM_TIMES( \
187 perceived_page_load_time, \
188 base::TimeDelta::FromMilliseconds(10), \
189 base::TimeDelta::FromSeconds(60), \
192 // Summary of all histograms Perceived PLT histograms:
193 // (all prefixed PerceivedPLT)
194 // PerceivedPLT -- Perceived Pageloadtimes (PPLT) for all pages in the group.
195 // ...Windowed -- PPLT for pages in the 30s after a prerender is created.
196 // ...Matched -- A prerendered page that was swapped in. In the NoUse
197 // and Control group cases, while nothing ever gets swapped in, we do keep
198 // track of what would be prerendered and would be swapped in -- and those
199 // cases are what is classified as Match for these groups.
200 // ...MatchedComplete -- A prerendered page that was swapped in + a few
201 // that were not swapped in so that the set of pages lines up more closely with
202 // the control group.
203 // ...FirstAfterMiss -- First page to finish loading after a prerender, which
204 // is different from the page that was prerendered.
205 // ...FirstAfterMissNonOverlapping -- Same as FirstAfterMiss, but only
206 // triggering for the first page to finish after the prerender that also started
207 // after the prerender started.
208 // ...FirstAfterMissBoth -- pages meeting
209 // FirstAfterMiss AND FirstAfterMissNonOverlapping
210 // ...FirstAfterMissAnyOnly -- pages meeting
211 // FirstAfterMiss but NOT FirstAfterMissNonOverlapping
212 // ..FirstAfterMissNonOverlappingOnly -- pages meeting
213 // FirstAfterMissNonOverlapping but NOT FirstAfterMiss
215 void PrerenderHistograms::RecordPerceivedPageLoadTime(
217 base::TimeDelta perceived_page_load_time
,
218 NavigationType navigation_type
,
220 if (!url
.SchemeIsHTTPOrHTTPS())
222 bool within_window
= WithinWindow();
223 bool is_google_url
= IsGoogleDomain(url
);
224 RECORD_PLT("PerceivedPLT", perceived_page_load_time
);
226 RECORD_PLT("PerceivedPLTWindowed", perceived_page_load_time
);
227 if (navigation_type
!= NAVIGATION_TYPE_NORMAL
) {
228 DCHECK(navigation_type
== NAVIGATION_TYPE_WOULD_HAVE_BEEN_PRERENDERED
||
229 navigation_type
== NAVIGATION_TYPE_PRERENDERED
);
230 RECORD_PLT("PerceivedPLTMatchedComplete", perceived_page_load_time
);
231 if (navigation_type
== NAVIGATION_TYPE_PRERENDERED
)
232 RECORD_PLT("PerceivedPLTMatched", perceived_page_load_time
);
233 seen_any_pageload_
= true;
234 seen_pageload_started_after_prerender_
= true;
235 } else if (within_window
) {
236 RECORD_PLT("PerceivedPLTWindowNotMatched", perceived_page_load_time
);
237 if (!is_google_url
) {
238 bool recorded_any
= false;
239 bool recorded_non_overlapping
= false;
240 if (!seen_any_pageload_
) {
241 seen_any_pageload_
= true;
242 RECORD_PLT("PerceivedPLTFirstAfterMiss", perceived_page_load_time
);
245 if (!seen_pageload_started_after_prerender_
&&
246 perceived_page_load_time
<= GetTimeSinceLastPrerender()) {
247 seen_pageload_started_after_prerender_
= true;
248 RECORD_PLT("PerceivedPLTFirstAfterMissNonOverlapping",
249 perceived_page_load_time
);
250 recorded_non_overlapping
= true;
252 if (recorded_any
|| recorded_non_overlapping
) {
253 if (recorded_any
&& recorded_non_overlapping
) {
254 RECORD_PLT("PerceivedPLTFirstAfterMissBoth",
255 perceived_page_load_time
);
256 } else if (recorded_any
) {
257 RECORD_PLT("PerceivedPLTFirstAfterMissAnyOnly",
258 perceived_page_load_time
);
259 } else if (recorded_non_overlapping
) {
260 RECORD_PLT("PerceivedPLTFirstAfterMissNonOverlappingOnly",
261 perceived_page_load_time
);
268 void PrerenderHistograms::RecordPageLoadTimeNotSwappedIn(
270 base::TimeDelta page_load_time
,
271 const GURL
& url
) const {
272 // If the URL to be prerendered is not a http[s] URL, or is a Google URL,
274 if (!url
.SchemeIsHTTPOrHTTPS() || IsGoogleDomain(url
))
276 RECORD_PLT("PrerenderNotSwappedInPLT", page_load_time
);
279 void PrerenderHistograms::RecordPercentLoadDoneAtSwapin(Origin origin
,
280 double fraction
) const {
281 if (fraction
< 0.0 || fraction
> 1.0)
283 int percentage
= static_cast<int>(fraction
* 100);
284 if (percentage
< 0 || percentage
> 100)
286 PREFIXED_HISTOGRAM("PercentLoadDoneAtSwapin",
287 origin
, UMA_HISTOGRAM_PERCENTAGE(name
, percentage
));
290 base::TimeDelta
PrerenderHistograms::GetTimeSinceLastPrerender() const {
291 return base::TimeTicks::Now() - last_prerender_seen_time_
;
294 bool PrerenderHistograms::WithinWindow() const {
295 if (last_prerender_seen_time_
.is_null())
297 return GetTimeSinceLastPrerender() <=
298 base::TimeDelta::FromSeconds(kWindowDurationSeconds
);
301 void PrerenderHistograms::RecordTimeUntilUsed(
303 base::TimeDelta time_until_used
) const {
305 "TimeUntilUsed2", origin
,
306 UMA_HISTOGRAM_CUSTOM_TIMES(
309 base::TimeDelta::FromMilliseconds(10),
310 base::TimeDelta::FromMinutes(30),
314 void PrerenderHistograms::RecordAbandonTimeUntilUsed(
316 base::TimeDelta time_until_used
) const {
318 "AbandonTimeUntilUsed", origin
,
319 UMA_HISTOGRAM_CUSTOM_TIMES(
322 base::TimeDelta::FromMilliseconds(10),
323 base::TimeDelta::FromSeconds(30),
327 void PrerenderHistograms::RecordPerSessionCount(Origin origin
,
330 "PrerendersPerSessionCount", origin
,
331 UMA_HISTOGRAM_COUNTS(name
, count
));
334 void PrerenderHistograms::RecordTimeBetweenPrerenderRequests(
335 Origin origin
, base::TimeDelta time
) const {
337 "TimeBetweenPrerenderRequests", origin
,
338 UMA_HISTOGRAM_TIMES(name
, time
));
341 void PrerenderHistograms::RecordFinalStatus(
343 PrerenderContents::MatchCompleteStatus mc_status
,
344 FinalStatus final_status
) const {
345 DCHECK(final_status
!= FINAL_STATUS_MAX
);
347 if (mc_status
== PrerenderContents::MATCH_COMPLETE_DEFAULT
||
348 mc_status
== PrerenderContents::MATCH_COMPLETE_REPLACED
) {
349 PREFIXED_HISTOGRAM_ORIGIN_EXPERIMENT(
350 "FinalStatus", origin
,
351 UMA_HISTOGRAM_ENUMERATION(name
, final_status
, FINAL_STATUS_MAX
));
353 if (mc_status
== PrerenderContents::MATCH_COMPLETE_DEFAULT
||
354 mc_status
== PrerenderContents::MATCH_COMPLETE_REPLACEMENT
||
355 mc_status
== PrerenderContents::MATCH_COMPLETE_REPLACEMENT_PENDING
) {
356 PREFIXED_HISTOGRAM_ORIGIN_EXPERIMENT(
357 "FinalStatusMatchComplete", origin
,
358 UMA_HISTOGRAM_ENUMERATION(name
, final_status
, FINAL_STATUS_MAX
));
362 void PrerenderHistograms::RecordNetworkBytes(Origin origin
,
364 int64 prerender_bytes
,
365 int64 profile_bytes
) {
366 const int kHistogramMin
= 1;
367 const int kHistogramMax
= 100000000; // 100M.
368 const int kBucketCount
= 50;
370 UMA_HISTOGRAM_CUSTOM_COUNTS("Prerender.NetworkBytesTotalForProfile",
376 if (prerender_bytes
== 0)
383 UMA_HISTOGRAM_CUSTOM_COUNTS(
384 name
, prerender_bytes
, kHistogramMin
, kHistogramMax
, kBucketCount
));
387 "NetworkBytesWasted",
389 UMA_HISTOGRAM_CUSTOM_COUNTS(
390 name
, prerender_bytes
, kHistogramMin
, kHistogramMax
, kBucketCount
));
394 bool PrerenderHistograms::IsOriginWash() const {
400 } // namespace prerender