blob: 11f19bc0de721e6cfa23b3572f1260a26b634a4b [file] [log] [blame]
# Copyright 2013 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.
import os
from core import path_util
from core import perf_benchmark
from page_sets import webgl_supported_shared_state
from telemetry import benchmark
from telemetry import page as page_module
from telemetry.page import legacy_page_test
from telemetry.page import shared_page_state
from telemetry import story
from telemetry.timeline import bounds
from telemetry.timeline import model as model_module
from telemetry.timeline import tracing_config
from telemetry.value import list_of_scalar_values
from telemetry.value import trace
BLINK_PERF_BASE_DIR = os.path.join(path_util.GetChromiumSrcDir(),
'third_party', 'WebKit', 'PerformanceTests')
SKIPPED_FILE = os.path.join(BLINK_PERF_BASE_DIR, 'Skipped')
def CreateStorySetFromPath(path, skipped_file,
shared_page_state_class=(
shared_page_state.SharedPageState)):
assert os.path.exists(path)
page_urls = []
serving_dirs = set()
def _AddPage(path):
if not path.endswith('.html'):
return
if '../' in open(path, 'r').read():
# If the page looks like it references its parent dir, include it.
serving_dirs.add(os.path.dirname(os.path.dirname(path)))
page_urls.append('file://' + path.replace('\\', '/'))
def _AddDir(dir_path, skipped):
for candidate_path in os.listdir(dir_path):
if candidate_path == 'resources':
continue
candidate_path = os.path.join(dir_path, candidate_path)
if candidate_path.startswith(skipped):
continue
if os.path.isdir(candidate_path):
_AddDir(candidate_path, skipped)
else:
_AddPage(candidate_path)
if os.path.isdir(path):
skipped = []
if os.path.exists(skipped_file):
for line in open(skipped_file, 'r').readlines():
line = line.strip()
if line and not line.startswith('#'):
skipped_path = os.path.join(os.path.dirname(skipped_file), line)
skipped.append(skipped_path.replace('/', os.sep))
_AddDir(path, tuple(skipped))
else:
_AddPage(path)
ps = story.StorySet(base_dir=os.getcwd() + os.sep,
serving_dirs=serving_dirs)
all_urls = [p.rstrip('/') for p in page_urls]
common_prefix = os.path.dirname(os.path.commonprefix(all_urls))
for url in page_urls:
name = url[len(common_prefix):].strip('/')
ps.AddStory(page_module.Page(
url, ps, ps.base_dir,
shared_page_state_class=shared_page_state_class,
name=name))
return ps
def _ComputeTraceEventsThreadTimeForBlinkPerf(
model, renderer_thread, trace_events_to_measure):
""" Compute the CPU duration for each of |trace_events_to_measure| during
blink_perf test.
Args:
renderer_thread: the renderer thread which run blink_perf test.
trace_events_to_measure: a list of string names of trace events to measure
CPU duration for.
Returns:
a dictionary in which each key is a trace event' name (from
|trace_events_to_measure| list), and value is a list of numbers that
represents to total cpu time of that trace events in each blink_perf test.
"""
trace_cpu_time_metrics = {}
# Collect the bounds of "blink_perf.runTest" events.
test_runs_bounds = []
for event in renderer_thread.async_slices:
if event.name == "blink_perf.runTest":
test_runs_bounds.append(bounds.Bounds.CreateFromEvent(event))
test_runs_bounds.sort(key=lambda b: b.min)
for t in trace_events_to_measure:
trace_cpu_time_metrics[t] = [0.0] * len(test_runs_bounds)
for event_name in trace_events_to_measure:
curr_test_runs_bound_index = 0
prev_event = None
for event in model.IterAllEventsOfName(event_name):
if prev_event and prev_event.end >= event.start:
continue
while (curr_test_runs_bound_index < len(test_runs_bounds) and
event.start > test_runs_bounds[curr_test_runs_bound_index].max):
curr_test_runs_bound_index += 1
if curr_test_runs_bound_index >= len(test_runs_bounds):
break
curr_test_bound = test_runs_bounds[curr_test_runs_bound_index]
intersect_wall_time = bounds.Bounds.GetOverlapBetweenBounds(
curr_test_bound, bounds.Bounds.CreateFromEvent(event))
if event.thread_duration and event.duration:
intersect_cpu_time = (
intersect_wall_time * event.thread_duration / event.duration)
else:
intersect_cpu_time = intersect_wall_time
trace_cpu_time_metrics[event_name][curr_test_runs_bound_index] += (
intersect_cpu_time)
prev_event = event
return trace_cpu_time_metrics
class _BlinkPerfMeasurement(legacy_page_test.LegacyPageTest):
"""Tuns a blink performance test and reports the results."""
def __init__(self):
super(_BlinkPerfMeasurement, self).__init__()
with open(os.path.join(os.path.dirname(__file__),
'blink_perf.js'), 'r') as f:
self._blink_perf_js = f.read()
def WillNavigateToPage(self, page, tab):
del tab # unused
page.script_to_evaluate_on_commit = self._blink_perf_js
def CustomizeBrowserOptions(self, options):
options.AppendExtraBrowserArgs([
'--js-flags=--expose_gc',
'--enable-experimental-web-platform-features',
# Note that both this flag:
'--ignore-autoplay-restrictions',
# and this flag:
'--disable-gesture-requirement-for-media-playback',
# should be used until every build from
# ToT to Stable switches over to one flag or another. This is to support
# reference builds.
# --disable-gesture-requirement-for-media-playback is the old one and
# can be removed after M60 goes to stable.
'--enable-experimental-canvas-features',
# TODO(qinmin): After fixing crbug.com/592017, remove this command line.
'--reduce-security-for-testing'
])
def SetOptions(self, options):
super(_BlinkPerfMeasurement, self).SetOptions(options)
if 'content-shell' in options.browser_options.browser_type:
options.AppendExtraBrowserArgs('--expose-internals-for-testing')
def _ContinueTestRunWithTracing(self, tab):
tracing_categories = tab.EvaluateJavaScript(
'testRunner.tracingCategories')
config = tracing_config.TracingConfig()
config.enable_chrome_trace = True
config.chrome_trace_config.category_filter.AddFilterString(
'blink.console') # This is always required for js land trace event
config.chrome_trace_config.category_filter.AddFilterString(
tracing_categories)
tab.browser.platform.tracing_controller.StartTracing(config)
tab.EvaluateJavaScript('testRunner.scheduleTestRun()')
tab.WaitForJavaScriptCondition('testRunner.isDone')
return tab.browser.platform.tracing_controller.StopTracing()
def PrintAndCollectTraceEventMetrics(self, trace_cpu_time_metrics, results):
unit = 'ms'
print
for trace_event_name, cpu_times in trace_cpu_time_metrics.iteritems():
print 'CPU times of trace event "%s":' % trace_event_name
cpu_times_string = ', '.join(['{0:.10f}'.format(t) for t in cpu_times])
print 'values %s %s' % (cpu_times_string, unit)
avg = 0.0
if cpu_times:
avg = sum(cpu_times)/len(cpu_times)
print 'avg', '{0:.10f}'.format(avg), unit
results.AddValue(list_of_scalar_values.ListOfScalarValues(
results.current_page, name=trace_event_name, units=unit,
values=cpu_times))
print
print '\n'
def ValidateAndMeasurePage(self, page, tab, results):
tab.WaitForJavaScriptCondition(
'testRunner.isDone || testRunner.isWaitingForTracingStart', timeout=600)
trace_cpu_time_metrics = {}
if tab.EvaluateJavaScript('testRunner.isWaitingForTracingStart'):
trace_data = self._ContinueTestRunWithTracing(tab)
trace_value = trace.TraceValue(page, trace_data)
results.AddValue(trace_value)
trace_events_to_measure = tab.EvaluateJavaScript(
'window.testRunner.traceEventsToMeasure')
model = model_module.TimelineModel(trace_data)
renderer_thread = model.GetRendererThreadFromTabId(tab.id)
trace_cpu_time_metrics = _ComputeTraceEventsThreadTimeForBlinkPerf(
model, renderer_thread, trace_events_to_measure)
log = tab.EvaluateJavaScript('document.getElementById("log").innerHTML')
for line in log.splitlines():
if line.startswith("FATAL: "):
print line
continue
if not line.startswith('values '):
continue
parts = line.split()
values = [float(v.replace(',', '')) for v in parts[1:-1]]
units = parts[-1]
metric = page.display_name.split('.')[0].replace('/', '_')
results.AddValue(list_of_scalar_values.ListOfScalarValues(
results.current_page, metric, units, values))
break
print log
self.PrintAndCollectTraceEventMetrics(trace_cpu_time_metrics, results)
class _BlinkPerfBenchmark(perf_benchmark.PerfBenchmark):
test = _BlinkPerfMeasurement
@classmethod
def Name(cls):
return 'blink_perf.' + cls.tag
def CreateStorySet(self, options):
path = os.path.join(BLINK_PERF_BASE_DIR, self.subdir)
return CreateStorySetFromPath(path, SKIPPED_FILE)
@benchmark.Owner(emails=['jbroman@chromium.org',
'yukishiino@chromium.org',
'haraken@chromium.org'])
class BlinkPerfBindings(_BlinkPerfBenchmark):
tag = 'bindings'
subdir = 'Bindings'
def GetExpectations(self):
class StoryExpectations(story.expectations.StoryExpectations):
def SetExpectations(self):
self.DisableStory(
'structured-clone-long-string-deserialize.html',
[story.expectations.ALL_ANDROID],
'crbug.com/528472')
self.DisableStory(
'structured-clone-long-string-serialize.html',
[story.expectations.ALL_ANDROID],
'crbug.com/528472')
self.DisableStory(
'structured-clone-json-serialize.html',
[story.expectations.ANDROID_ONE],
'crbug.com/736123')
self.DisableStory(
'structured-clone-json-deserialize.html',
[story.expectations.ANDROID_ONE],
'crbug.com/736123')
return StoryExpectations()
@benchmark.Owner(emails=['rune@opera.com'])
class BlinkPerfCSS(_BlinkPerfBenchmark):
tag = 'css'
subdir = 'CSS'
def GetExpectations(self):
class StoryExpectations(story.expectations.StoryExpectations):
def SetExpectations(self):
pass # Nothing disabled.
return StoryExpectations()
@benchmark.Disabled('android', # http://crbug.com/685320
'android-webview') # http://crbug.com/593200
@benchmark.Owner(emails=['junov@chromium.org'])
class BlinkPerfCanvas(_BlinkPerfBenchmark):
tag = 'canvas'
subdir = 'Canvas'
def CreateStorySet(self, options):
path = os.path.join(BLINK_PERF_BASE_DIR, self.subdir)
story_set = CreateStorySetFromPath(
path, SKIPPED_FILE,
shared_page_state_class=(
webgl_supported_shared_state.WebGLSupportedSharedState))
# WebGLSupportedSharedState requires the skipped_gpus property to
# be set on each page.
for page in story_set:
page.skipped_gpus = []
return story_set
def GetExpectations(self):
class StoryExpectations(story.expectations.StoryExpectations):
def SetExpectations(self):
self.PermanentlyDisableBenchmark(
[story.expectations.ANDROID_SVELTE], 'crbug.com/593973')
return StoryExpectations()
def SetExtraBrowserOptions(self, options):
options.AppendExtraBrowserArgs([
'--enable-color-correct-rendering',
])
@benchmark.Owner(emails=['jbroman@chromium.org',
'yukishiino@chromium.org',
'haraken@chromium.org'])
class BlinkPerfDOM(_BlinkPerfBenchmark):
tag = 'dom'
subdir = 'DOM'
def GetExpectations(self):
class StoryExpectations(story.expectations.StoryExpectations):
def SetExpectations(self):
pass # Nothing disabled.
return StoryExpectations()
@benchmark.Owner(emails=['hayato@chromium.org'])
class BlinkPerfEvents(_BlinkPerfBenchmark):
tag = 'events'
subdir = 'Events'
def GetExpectations(self):
class StoryExpectations(story.expectations.StoryExpectations):
def SetExpectations(self):
pass # Nothing disabled.
return StoryExpectations()
@benchmark.Disabled('win8') # http://crbug.com/462350
@benchmark.Owner(emails=['eae@chromium.org'])
class BlinkPerfLayout(_BlinkPerfBenchmark):
tag = 'layout'
subdir = 'Layout'
def GetExpectations(self):
class Expectations(story.expectations.StoryExpectations):
def SetExpectations(self):
self.PermanentlyDisableBenchmark(
[story.expectations.ANDROID_SVELTE], 'crbug.com/551950')
return Expectations()
@benchmark.Owner(emails=['wangxianzhu@chromium.org'])
class BlinkPerfPaint(_BlinkPerfBenchmark):
tag = 'paint'
subdir = 'Paint'
def GetExpectations(self):
class StoryExpectations(story.expectations.StoryExpectations):
def SetExpectations(self):
self.PermanentlyDisableBenchmark(
[story.expectations.ANDROID_SVELTE], 'crbug.com/574483')
return StoryExpectations()
@benchmark.Disabled('win') # crbug.com/488493
@benchmark.Owner(emails=['jbroman@chromium.org',
'yukishiino@chromium.org',
'haraken@chromium.org'])
class BlinkPerfParser(_BlinkPerfBenchmark):
tag = 'parser'
subdir = 'Parser'
def GetExpectations(self):
class StoryExpectations(story.expectations.StoryExpectations):
def SetExpectations(self):
pass # Nothing disabled.
return StoryExpectations()
@benchmark.Owner(emails=['kouhei@chromium.org', 'fs@opera.com'])
class BlinkPerfSVG(_BlinkPerfBenchmark):
tag = 'svg'
subdir = 'SVG'
def GetExpectations(self):
class StoryExpectations(story.expectations.StoryExpectations):
def SetExpectations(self):
pass # Nothing disabled.
return StoryExpectations()
@benchmark.Owner(emails=['hayato@chromium.org'])
class BlinkPerfShadowDOM(_BlinkPerfBenchmark):
tag = 'shadow_dom'
subdir = 'ShadowDOM'
def GetExpectations(self):
class StoryExpectations(story.expectations.StoryExpectations):
def SetExpectations(self):
self.PermanentlyDisableBenchmark(
[story.expectations.ANDROID_NEXUS5X], 'crbug.com/702319')
return StoryExpectations()