blob: b72c2264c9c488d5c93877519da00ba48ea562bf [file] [log] [blame]
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
Cache temperature specifies how the browser cache should be configured before
the page run.
See design doc for details:
https://docs.google.com/document/u/1/d/12D7tkhZi887g9d0U2askU9JypU_wYiEI7Lw0bfwxUgA
"""
import logging
from telemetry.core import exceptions
# Default Cache Temperature. The page doesn't care which browser cache state
# it is run on.
ANY = 'any'
# Emulates cold runs. Clears various caches and data with using tab.ClearCache()
# and tab.ClearDataForOrigin().
COLD = 'cold'
# Emulates warm browser runs. Ensures that the page was visited once in a
# different renderer.
WARM_BROWSER = 'warm-browser'
# Emulates hot runs. Ensures that the page was visited at least twice in a
# different renderer before the run.
HOT_BROWSER = 'hot-browser'
# Emulates warm renderer runs. Ensures that the page was visited once before the
# run in the same renderer.
WARM = 'warm'
# Emulates hot renderer runs. Ensures that the page was visited at least twice
# in the same renderer before the run.
HOT = 'hot'
ALL_CACHE_TEMPERATURES = [ANY, COLD, WARM_BROWSER, HOT_BROWSER, WARM, HOT]
class _MarkTelemetryInternal(object):
def __init__(self, tab, identifier):
self.tab = tab
self.identifier = identifier
def __enter__(self):
# This marker must match the regexp in
# ChromeProcessHelper.findTelemetryInternalRanges_().
self.tab.AddTimelineMarker('telemetry.internal.%s.start' % self.identifier)
return self
def __exit__(self, exception_type, exception_value, traceback):
if exception_type:
return True
# This marker must match the regexp in
# ChromeProcessHelper.findTelemetryInternalRanges_().
self.tab.AddTimelineMarker('telemetry.internal.%s.end' % self.identifier)
return True
def _ClearCacheAndData(tab, url):
tab.ClearCache(force=True)
tab.ClearDataForOrigin(url)
def _WarmCache(page, tab, temperature):
with _MarkTelemetryInternal(tab, 'warm_cache.%s' % temperature):
page.RunNavigateSteps(tab.action_runner)
page.RunPageInteractions(tab.action_runner)
tab.Navigate("about:blank")
tab.WaitForDocumentReadyStateToBeComplete()
# Stop service worker after each cache warming to ensure service worker
# script evaluation will be executed again in next navigation.
tab.StopAllServiceWorkers()
class CacheManipulator(object):
RENDERER_TEMPERATURE = None
BROWSER_TEMPERATURE = None
@staticmethod
def PrepareRendererCache(page, tab, previous_page):
raise NotImplementedError
@classmethod
def PrepareBrowserCache(cls, page, browser, previous_page):
# Perform browser cache manipulation in a different tab.
tab = browser.tabs.New()
cls.PrepareRendererCache(page, tab, previous_page)
tab.Close()
# Flush and discard current tracing data, so the trace does not contain
# events from the tab just closed.
browser.platform.tracing_controller.FlushTracing(discard_current=True)
class AnyCacheManipulator(CacheManipulator):
RENDERER_TEMPERATURE = ANY
BROWSER_TEMPERATURE = None
@staticmethod
def PrepareRendererCache(page, tab, previous_page):
pass
@classmethod
def PrepareBrowserCache(cls, page, browser, previous_page):
raise exceptions.Error('Prepare browser cache not supported')
class ColdCacheManipulator(CacheManipulator):
RENDERER_TEMPERATURE = COLD
BROWSER_TEMPERATURE = None
@staticmethod
def PrepareRendererCache(page, tab, previous_page):
if previous_page is None:
# DiskCache initialization is performed asynchronously on Chrome start-up.
# Ensure that DiskCache is initialized before starting the measurement to
# avoid performance skew.
# This is done by navigating to an inexistent URL and then wait for the
# navigation to complete.
# TODO(kouhei) Consider moving this logic to PageCyclerStory
with _MarkTelemetryInternal(tab, 'ensure_diskcache'):
tab.Navigate("http://does.not.exist")
tab.WaitForDocumentReadyStateToBeComplete()
_ClearCacheAndData(tab, page.url)
@classmethod
def PrepareBrowserCache(cls, page, browser, previous_page):
raise exceptions.Error('Prepare browser cache not supported')
class WarmCacheManipulator(CacheManipulator):
RENDERER_TEMPERATURE = WARM
BROWSER_TEMPERATURE = WARM_BROWSER
@staticmethod
def PrepareRendererCache(page, tab, previous_page):
if (previous_page is not None and
previous_page.url == page.url and
previous_page.cache_temperature == COLD):
if '#' in page.url:
# TODO(crbug.com/768780): Move this operation to tab.Navigate().
# This navigates to inexistent URL to avoid in-page hash navigation.
# Note: Unlike PCv1, PCv2 iterates the same URL for different cache
# configurations. This may issue blink in-page hash navigations,
# which isn't intended here.
with _MarkTelemetryInternal(tab, 'avoid_double_hash_navigation'):
tab.Navigate("http://does.not.exist")
tab.WaitForDocumentReadyStateToBeComplete()
# Stop all service workers before running tests to measure the starting
# time of service worker too.
tab.StopAllServiceWorkers()
else:
_ClearCacheAndData(tab, page.url)
_WarmCache(page, tab, WARM)
class HotCacheManipulator(CacheManipulator):
RENDERER_TEMPERATURE = HOT
BROWSER_TEMPERATURE = HOT_BROWSER
@staticmethod
def PrepareRendererCache(page, tab, previous_page):
if (previous_page is not None and
previous_page.url == page.url and
previous_page.cache_temperature != ANY):
if previous_page.cache_temperature == COLD:
_WarmCache(page, tab, HOT)
else:
if '#' in page.url:
# TODO(crbug.com/768780): Move this operation to tab.Navigate().
# This navigates to inexistent URL to avoid in-page hash navigation.
# Note: Unlike PCv1, PCv2 iterates the same URL for different cache
# configurations. This may issue blink in-page hash navigations,
# which isn't intended here.
with _MarkTelemetryInternal(tab, 'avoid_double_hash_navigation'):
tab.Navigate("http://does.not.exist")
tab.WaitForDocumentReadyStateToBeComplete()
# Stop all service workers before running tests to measure the starting
# time of service worker too.
tab.StopAllServiceWorkers()
else:
_ClearCacheAndData(tab, page.url)
_WarmCache(page, tab, WARM)
_WarmCache(page, tab, HOT)
def EnsurePageCacheTemperature(page, browser, previous_page=None):
temperature = page.cache_temperature
logging.info('PageCacheTemperature: %s', temperature)
for c in [AnyCacheManipulator, ColdCacheManipulator, WarmCacheManipulator,
HotCacheManipulator]:
if temperature == c.RENDERER_TEMPERATURE:
c.PrepareRendererCache(page, browser.tabs[0], previous_page)
return
elif temperature == c.BROWSER_TEMPERATURE:
c.PrepareBrowserCache(page, browser, previous_page)
return
raise NotImplementedError('Unrecognized cache temperature: %s' % temperature)