Unwind the URL-based experiment IDs.
[chromium-blink-merge.git] / chrome / browser / prerender / prerender_histograms.cc
blob46a35ff9680ac2d76c7e2c91574ecd7f271f9cca
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"
7 #include <string>
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;
18 namespace prerender {
20 namespace {
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) {
37 if (is_wash)
38 return ComposeHistogramName("wash", name);
40 switch (origin) {
41 case ORIGIN_OMNIBOX:
42 return ComposeHistogramName("omnibox", name);
43 case ORIGIN_NONE:
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);
51 case ORIGIN_INSTANT:
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);
57 default:
58 NOTREACHED();
59 break;
62 // Dummy return value to make the compiler happy.
63 NOTREACHED();
64 return ComposeHistogramName("wash", name);
67 bool OriginIsOmnibox(Origin origin) {
68 return origin == ORIGIN_OMNIBOX;
71 } // namespace
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, \
80 HISTOGRAM) \
81 PREFIXED_HISTOGRAM_INTERNAL(origin, false, HISTOGRAM, histogram_name)
83 #define PREFIXED_HISTOGRAM_INTERNAL(origin, wash, HISTOGRAM, histogram_name) \
84 do { \
85 { \
86 /* Do not rename. HISTOGRAM expects a local variable "name". */ \
87 std::string name = ComposeHistogramName(std::string(), histogram_name); \
88 HISTOGRAM; \
89 } \
90 /* Do not rename. HISTOGRAM expects a local variable "name". */ \
91 std::string name = GetHistogramName(origin, wash, histogram_name); \
92 if (wash) { \
93 HISTOGRAM; \
94 } else if (origin == ORIGIN_OMNIBOX) { \
95 HISTOGRAM; \
96 } else if (origin == ORIGIN_NONE) { \
97 HISTOGRAM; \
98 } else if (origin == ORIGIN_LINK_REL_PRERENDER_SAMEDOMAIN) { \
99 HISTOGRAM; \
100 } else if (origin == ORIGIN_LINK_REL_PRERENDER_CROSSDOMAIN) { \
101 HISTOGRAM; \
102 } else if (origin == ORIGIN_EXTERNAL_REQUEST) { \
103 HISTOGRAM; \
104 } else if (origin == ORIGIN_INSTANT) { \
105 HISTOGRAM; \
106 } else if (origin == ORIGIN_LINK_REL_NEXT) { \
107 HISTOGRAM; \
108 } else { \
109 HISTOGRAM; \
111 } while (0)
113 PrerenderHistograms::PrerenderHistograms()
114 : last_origin_(ORIGIN_MAX),
115 origin_wash_(false),
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;
126 } else {
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
129 // the previous one.
130 if (origin != last_origin_)
131 origin_wash_ = true;
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(
170 Origin origin,
171 base::TimeDelta delta) const {
172 PREFIXED_HISTOGRAM(
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( \
184 tag, origin, \
185 UMA_HISTOGRAM_CUSTOM_TIMES( \
186 name, \
187 perceived_page_load_time, \
188 base::TimeDelta::FromMilliseconds(10), \
189 base::TimeDelta::FromSeconds(60), \
190 100))
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(
216 Origin origin,
217 base::TimeDelta perceived_page_load_time,
218 NavigationType navigation_type,
219 const GURL& url) {
220 if (!url.SchemeIsHTTPOrHTTPS())
221 return;
222 bool within_window = WithinWindow();
223 bool is_google_url = IsGoogleDomain(url);
224 RECORD_PLT("PerceivedPLT", perceived_page_load_time);
225 if (within_window)
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);
243 recorded_any = true;
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(
269 Origin origin,
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,
273 // do not record.
274 if (!url.SchemeIsHTTPOrHTTPS() || IsGoogleDomain(url))
275 return;
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)
282 return;
283 int percentage = static_cast<int>(fraction * 100);
284 if (percentage < 0 || percentage > 100)
285 return;
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())
296 return false;
297 return GetTimeSinceLastPrerender() <=
298 base::TimeDelta::FromSeconds(kWindowDurationSeconds);
301 void PrerenderHistograms::RecordTimeUntilUsed(
302 Origin origin,
303 base::TimeDelta time_until_used) const {
304 PREFIXED_HISTOGRAM(
305 "TimeUntilUsed2", origin,
306 UMA_HISTOGRAM_CUSTOM_TIMES(
307 name,
308 time_until_used,
309 base::TimeDelta::FromMilliseconds(10),
310 base::TimeDelta::FromMinutes(30),
311 50));
314 void PrerenderHistograms::RecordAbandonTimeUntilUsed(
315 Origin origin,
316 base::TimeDelta time_until_used) const {
317 PREFIXED_HISTOGRAM(
318 "AbandonTimeUntilUsed", origin,
319 UMA_HISTOGRAM_CUSTOM_TIMES(
320 name,
321 time_until_used,
322 base::TimeDelta::FromMilliseconds(10),
323 base::TimeDelta::FromSeconds(30),
324 50));
327 void PrerenderHistograms::RecordPerSessionCount(Origin origin,
328 int count) const {
329 PREFIXED_HISTOGRAM(
330 "PrerendersPerSessionCount", origin,
331 UMA_HISTOGRAM_COUNTS(name, count));
334 void PrerenderHistograms::RecordTimeBetweenPrerenderRequests(
335 Origin origin, base::TimeDelta time) const {
336 PREFIXED_HISTOGRAM(
337 "TimeBetweenPrerenderRequests", origin,
338 UMA_HISTOGRAM_TIMES(name, time));
341 void PrerenderHistograms::RecordFinalStatus(
342 Origin origin,
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,
363 bool used,
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",
371 profile_bytes,
372 kHistogramMin,
373 1000000000, // 1G
374 kBucketCount);
376 if (prerender_bytes == 0)
377 return;
379 if (used) {
380 PREFIXED_HISTOGRAM(
381 "NetworkBytesUsed",
382 origin,
383 UMA_HISTOGRAM_CUSTOM_COUNTS(
384 name, prerender_bytes, kHistogramMin, kHistogramMax, kBucketCount));
385 } else {
386 PREFIXED_HISTOGRAM(
387 "NetworkBytesWasted",
388 origin,
389 UMA_HISTOGRAM_CUSTOM_COUNTS(
390 name, prerender_bytes, kHistogramMin, kHistogramMax, kBucketCount));
394 bool PrerenderHistograms::IsOriginWash() const {
395 if (!WithinWindow())
396 return false;
397 return origin_wash_;
400 } // namespace prerender