1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
|
# Copyright 2018 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import contextlib
import logging
from core import perf_benchmark
from core import platforms
from telemetry.core import android_platform
from telemetry.core import util as core_util
from telemetry.internal.browser import browser_finder
from telemetry.internal.platform import android_device
from telemetry.timeline import chrome_trace_category_filter
from telemetry.util import wpr_modes
from telemetry.web_perf import timeline_based_measurement
from telemetry import benchmark
from telemetry import story as story_module
# The import error below is mysterious: it produces no detailed error message,
# while appending a proper sys.path does not help.
from devil.android.sdk import intent # pylint: disable=import-error
# Chrome Startup Benchmarks for mobile devices (running Android).
#
# It uses specifics of AndroidPlatform and hardcodes sending Android intents. It
# should be disabled on non-Android to avoid failures at
# benchmark_smoke_unittest.BenchmarkSmokeTest.
#
# === Mini-HOWTO.
#
# 1. Configure for Release Official flavor to get the most representative
# results:
# shell> gn gen --args='use_remoteexec=true target_os="android" target_cpu="arm" \
# is_debug=false is_official_build=true' out/AndroidReleaseOfficial
#
# 2.1. Build Monochrome:
# shell> autoninja -C out/AndroidReleaseOfficial monochrome_apk
#
# 2.2. Build the (pseudo) PWA launcher (it will be auto-installed later):
# shell> autoninja -C out/AndroidReleaseOfficial/ webapk
#
# 3. Invoke Telemetry:
# shell> CHROMIUM_OUTPUT_DIR=out/AndroidReleaseOfficial \
# tools/perf/run_benchmark -v startup.mobile \
# --browser=android-chrome \
# --output-dir=/tmp/avoid-polluting-chrome-tree \
# --also-run-disabled-tests
#
# Important notes on the flags:
# --also-run-disabled-tests - is necessary because the benchmark is disabled in
# expectations.config to avoid failures on Android versions below M. This
# override is also used on internal bots. See: http://crbug.com/894744 and
# http://crbug.com/849907.
# --browser=android-chrome - *must* be used *instead* of "android-chromium". The
# latter may silently produce subtly incorrect results. This is because
# MonohromePublic initialization path is less optimized than Monochrome
# (no orderfile, no library prefetch, etc.)
# -v - in some cases the benchmark does not run in non-verbose mode, details
# unknown.
#
# Recording a WPR archive and uploading it:
# shell> CHROMIUM_OUTPUT_DIR=out/AndroidReleaseOfficial tools/perf/record_wpr \
# mobile_startup_benchmark --browser=android-chrome \
# --also-run-disabled-tests \
# --story-filter=mobile_pwa:with_http_cache \
# --output-dir=/tmp/mobile_pwa_output --upload
# Note: "startup_mobile_benchmark" instead of "startup.mobile".
_NUMBER_OF_ITERATIONS = 10
_MAX_BATTERY_TEMP = 32
class _MobileStartupSharedState(story_module.SharedState):
def __init__(self, test, finder_options, story_set, possible_browser=None):
"""
Args:
test: opaquely passed to parent class constructor.
finder_options: A BrowserFinderOptions object.
story_set: opaquely passed to parent class constructor.
"""
super(_MobileStartupSharedState, self).__init__(
test, finder_options, story_set, possible_browser)
self._finder_options = finder_options
if not self._possible_browser:
self._possible_browser = browser_finder.FindBrowser(self._finder_options)
self._current_story = None
# Allow using this shared state only on Android.
assert isinstance(self.platform, android_platform.AndroidPlatform)
self._finder_options.browser_options.browser_user_agent_type = 'mobile'
self._finder_options.browser_options.AppendExtraBrowserArgs(
'--skip-webapk-verification')
self.platform.Initialize()
self.platform.SetPerformanceMode(finder_options.performance_mode)
self._perf_mode_set = (finder_options.performance_mode !=
android_device.KEEP_PERFORMANCE_MODE)
webapk = core_util.FindLatestApkOnHost(finder_options.chrome_root,
'WebApk.apk')
if not webapk:
raise Exception('WebApk not found! Follow the Mini-HOWTO in '
'startup_mobile.py' + finder_options.chrome_root)
self.platform.InstallApplication(webapk)
wpr_mode = wpr_modes.WPR_REPLAY
self._number_of_iterations = _NUMBER_OF_ITERATIONS
if finder_options.use_live_sites:
wpr_mode = wpr_modes.WPR_OFF
elif finder_options.browser_options.wpr_mode == wpr_modes.WPR_RECORD:
wpr_mode = wpr_modes.WPR_RECORD
# When recording a WPR archive only load the story page once.
self._number_of_iterations = 1
self.platform.network_controller.Open(wpr_mode)
self._story_set = story_set
@property
def number_of_iterations(self):
return self._number_of_iterations
@property
def platform(self):
return self._possible_browser.platform
def TearDownState(self):
self.platform.network_controller.Close()
if self._perf_mode_set:
self.platform.SetPerformanceMode(android_device.NORMAL_PERFORMANCE_MODE)
def LaunchBrowser(self, url, flush_caches):
if flush_caches:
self.platform.FlushDnsCache()
self._possible_browser.FlushOsPageCaches()
self.platform.WaitForBatteryTemperature(_MAX_BATTERY_TEMP)
self.platform.StartActivity(
intent.Intent(package=self._possible_browser.settings.package,
activity=self._possible_browser.settings.activity,
data=url,
action='android.intent.action.VIEW'),
blocking=True)
def LaunchCCT(self, url):
self.platform.FlushDnsCache()
self._possible_browser.FlushOsPageCaches()
self.platform.WaitForBatteryTemperature(_MAX_BATTERY_TEMP)
# Note: The presence of the extra.SESSION extra defines a CCT intent.
cct_extras = {'android.support.customtabs.extra.SESSION': None}
self.platform.StartActivity(
intent.Intent(package=self._possible_browser.settings.package,
activity=self._possible_browser.settings.activity,
data=url, extras=cct_extras,
action='android.intent.action.VIEW'),
blocking=True)
def LaunchMobilePwa(self):
# Launches a bound webapk. The APK should be installed by the shared state
# constructor. Upon launch, Chrome extracts the icon and the URL from the
# APK.
self.platform.WaitForBatteryTemperature(_MAX_BATTERY_TEMP)
self.platform.StartActivity(intent.Intent(
package='org.chromium.webapk',
activity='org.chromium.webapk.shell_apk.h2o.H2OOpaqueMainActivity',
category='android.intent.category.LAUNCHER',
action='android.intent.action.MAIN'),
blocking=True)
@contextlib.contextmanager
def FindBrowser(self):
"""Find and manage the lifetime of a browser.
The browser is closed when exiting the context managed code, and the
browser state is dumped in case of errors during the story execution.
"""
browser = self._possible_browser.FindExistingBrowser()
try:
yield browser
except Exception as exc:
logging.critical(
'%s raised during story run. Dumping current browser state to help'
' diagnose this issue.', type(exc).__name__)
browser.DumpStateUponFailure()
raise
finally:
browser.Close()
def WillRunStory(self, story):
self.platform.network_controller.StartReplay(
self._story_set.WprFilePathForStory(story))
# Note: There is no need in StopReplay(), the |network_controller| will do
# it on Close().
self._possible_browser.SetUpEnvironment(
self._finder_options.browser_options)
self._current_story = story
def RunStory(self, _):
self._current_story.Run(self)
def DidRunStory(self, _):
self._current_story = None
self._possible_browser.CleanUpEnvironment()
def DumpStateUponStoryRunFailure(self, results):
del results
# Note: Dumping state of objects upon errors, e.g. of the browser, is
# handled individually by the context managers that handle their lifetime.
def CanRunStory(self, _):
return True
def _DriveMobileStartupWithIntent(shared_state, flush_caches):
for _ in range(shared_state.number_of_iterations):
# TODO(pasko): Find a way to fail the benchmark when WPR is set up
# incorrectly and error pages get loaded.
shared_state.LaunchBrowser('http://bbc.co.uk', flush_caches)
with shared_state.FindBrowser() as browser:
action_runner = browser.foreground_tab.action_runner
action_runner.tab.WaitForDocumentReadyStateToBeComplete()
class _MobileStartupWithIntentStory(story_module.Story):
def __init__(self):
super(_MobileStartupWithIntentStory, self).__init__(
_MobileStartupSharedState, name='intent:coldish:bbc')
def Run(self, shared_state):
_DriveMobileStartupWithIntent(shared_state, flush_caches=True)
class _MobileStartupWithIntentStoryWarm(story_module.Story):
def __init__(self):
super(_MobileStartupWithIntentStoryWarm, self).__init__(
_MobileStartupSharedState, name='intent:warm:bbc')
def Run(self, shared_state):
_DriveMobileStartupWithIntent(shared_state, flush_caches=False)
class _MobileStartupWithCctIntentStory(story_module.Story):
def __init__(self):
super(_MobileStartupWithCctIntentStory, self).__init__(
_MobileStartupSharedState, name='cct:coldish:bbc')
def Run(self, shared_state):
for _ in range(shared_state.number_of_iterations):
shared_state.LaunchCCT('http://bbc.co.uk')
with shared_state.FindBrowser() as browser:
action_runner = browser.foreground_tab.action_runner
action_runner.tab.WaitForDocumentReadyStateToBeComplete()
class _MobilePwaStartupStory(story_module.Story):
def __init__(self):
super(_MobilePwaStartupStory,
self).__init__(_MobileStartupSharedState,
name='mobile_pwa:with_http_cache')
def Run(self, shared_state):
for _ in range(shared_state.number_of_iterations):
# TODO(pasko): Flush HTTP cache for 'mobile_pwa:no_http_cache'.
shared_state.LaunchMobilePwa()
with shared_state.FindBrowser() as browser:
action_runner = browser.foreground_tab.action_runner
action_runner.tab.WaitForDocumentReadyStateToBeComplete()
class _MobileStartupStorySet(story_module.StorySet):
def __init__(self):
super(_MobileStartupStorySet, self).__init__(
archive_data_file='../page_sets/data/startup_pages.json',
cloud_storage_bucket=story_module.PARTNER_BUCKET)
self.AddStory(_MobileStartupWithIntentStory())
self.AddStory(_MobileStartupWithIntentStoryWarm())
self.AddStory(_MobileStartupWithCctIntentStory())
self.AddStory(_MobilePwaStartupStory())
@benchmark.Info(emails=['pasko@chromium.org', 'lizeb@chromium.org'],
component='Speed>Metrics>SystemHealthRegressions')
class MobileStartupBenchmark(perf_benchmark.PerfBenchmark):
"""Startup benchmark for Chrome on Android."""
# TODO(johnchen): Remove either the SUPPORTED_PLATFORMS or
# SUPPORTED_PLATFORMS_TAGS lists. Only one is necessary.
SUPPORTED_PLATFORM_TAGS = [platforms.ANDROID_NOT_WEBVIEW]
SUPPORTED_PLATFORMS = [story_module.expectations.ANDROID_NOT_WEBVIEW]
def CreateCoreTimelineBasedMeasurementOptions(self):
cat_filter = chrome_trace_category_filter.ChromeTraceCategoryFilter(
filter_string=('navigation,loading,net,netlog,network,offline_pages,'
'startup,toplevel,Java,EarlyJava'))
options = timeline_based_measurement.Options(cat_filter)
options.config.enable_chrome_trace = True
options.SetTimelineBasedMetrics([
'tracingMetric',
'androidStartupMetric',
])
return options
def CreateStorySet(self, options):
return _MobileStartupStorySet()
@classmethod
def Name(cls):
return 'startup.mobile'
def SetExtraBrowserOptions(self, options):
super(MobileStartupBenchmark, self).SetExtraBrowserOptions(options)
# Force online state for the offline indicator so it doesn't show and affect
# the benchmarks on bots, which are offline by default.
options.AppendExtraBrowserArgs(
'--force-online-connection-state-for-indicator')
|