Bug 1473997 - find a way to measure the battery usage of geckoview (:bc's initial patch); r=jmaher

Differential Revision: https://phabricator.services.mozilla.com/D14997

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Rob Wood 2018-12-19 20:52:28 +00:00
parent b180b8986c
commit c2349ad0f8
5 changed files with 132 additions and 3 deletions

View File

@ -127,6 +127,11 @@ class Raptor(TestingMixin, MercurialScript, CodeCoverageMixin, AndroidMixin):
"dest": "host",
"help": "Hostname from which to serve urls (default: 127.0.0.1).",
}],
[["--power-test"], {
"dest": "power_test",
"help": "Use Raptor to measure power usage. Currently supported only when "
"--host specified for geckoview.",
}],
[["--debug-mode"], {
"dest": "debug_mode",
"action": "store_true",
@ -196,6 +201,7 @@ class Raptor(TestingMixin, MercurialScript, CodeCoverageMixin, AndroidMixin):
self.gecko_profile_entries = self.config.get('gecko_profile_entries')
self.test_packages_url = self.config.get('test_packages_url')
self.host = self.config.get('host')
self.power_test = self.config.get('power_test')
self.is_release_build = self.config.get('is_release_build')
self.debug_mode = self.config.get('debug_mode', False)
@ -352,6 +358,8 @@ class Raptor(TestingMixin, MercurialScript, CodeCoverageMixin, AndroidMixin):
options.extend(['--code-coverage'])
if self.config.get('is_release_build', False):
options.extend(['--is-release-build'])
if self.config.get('power_test', False):
options.extend(['--power-test'])
for key, value in kw_options.items():
options.extend(['--%s' % key, value])

View File

@ -56,6 +56,7 @@ class RaptorRunner(MozbuildObject):
self.python_interp = sys.executable
self.raptor_args = raptor_args
self.host = kwargs['host']
self.power_test = kwargs['power_test']
self.is_release_build = kwargs['is_release_build']
def setup_benchmarks(self):
@ -133,6 +134,7 @@ class RaptorRunner(MozbuildObject):
'win64': 'python3_x64.manifest',
},
'host': self.host,
'power_test': self.power_test,
'is_release_build': self.is_release_build,
}
@ -177,6 +179,7 @@ class MachRaptor(MachCommandBase):
if conditions.is_android(build_obj) or kwargs['app'] == 'geckoview':
from mozrunner.devices.android_device import verify_android_device
from mozdevice import ADBAndroid, ADBHost
if not verify_android_device(build_obj, install=True, app=kwargs['binary']):
return 1
@ -187,7 +190,28 @@ class MachRaptor(MachCommandBase):
raptor = self._spawn(RaptorRunner)
try:
if kwargs['app'] == 'geckoview' and kwargs['power_test']:
device = ADBAndroid(verbose=True)
adbhost = ADBHost(verbose=True)
device_serial = "%s:5555" % device.get_ip_address()
device.command_output(["tcpip", "5555"])
raw_input("Please disconnect your device from USB then press ENTER...")
adbhost.command_output(["connect", device_serial])
while len(adbhost.devices()) > 1:
raw_input("You must disconnect your device from USB before continuing.")
# must reset the environment DEVICE_SERIAL which was set during
# verify_android_device to match our new tcpip value.
os.environ["DEVICE_SERIAL"] = device_serial
return raptor.run_test(sys.argv[2:], kwargs)
except Exception as e:
print(str(e))
return 1
finally:
try:
if kwargs['app'] == 'geckoview' and kwargs['power_test']:
raw_input("Connect device via usb and press ENTER...")
device = ADBAndroid(device=device_serial, verbose=True)
device.command_output(["usb"])
adbhost.command_output(["disconnect", device_serial])
except Exception:
adbhost.command_output(["kill-server"])

View File

@ -23,6 +23,9 @@ def create_parser(mach_interface=False):
add_arg('--host', dest='host',
help="Hostname from which to serve urls, defaults to 127.0.0.1.",
default='127.0.0.1')
add_arg('--power-test', dest="power_test", action="store_true",
help="Use Raptor to measure power usage. Currently supported only when "
"--host specified for geckoview.")
add_arg('--is-release-build', dest="is_release_build", default=False,
action='store_true',
help="Whether the build is a release build which requires work arounds "
@ -78,6 +81,12 @@ def verify_options(parser, args):
if args.gecko_profile is True and args.app != "firefox":
parser.error("Gecko profiling is only supported when running raptor on Firefox!")
# if --power-test specified, must be on geckview with --host specified.
if args.power_test:
if args.app != "geckoview" or args.host in ('localhost', '127.0.0.1'):
parser.error("Power test is only supported when running raptor on Geckoview "
"when host is specified!")
def parse_args(argv=None):
parser = create_parser()

View File

@ -0,0 +1,77 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from __future__ import absolute_import
import os
import re
def init_geckoview_power_test(raptor):
upload_dir = os.getenv('MOZ_UPLOAD_DIR')
if not upload_dir:
raptor.log.critical("Geckoview power test ignored because MOZ_UPLOAD_DIR was not set")
return
# Set the screen off timeout to 2 hours since the device will be running
# disconnected and would otherwise turn off the screen thereby halting
# execution of the test. Save the current value so we can restore it later
# since it is a persistent change.
raptor.screen_off_timeout = raptor.device.shell_output(
"settings get system screen_off_timeout").strip()
raptor.device.shell_output("settings put system screen_off_timeout 7200000")
raptor.device.shell_output("dumpsys batterystats --reset")
raptor.device.shell_output("dumpsys batterystats --enable full-wake-history")
filepath = os.path.join(upload_dir, 'battery-before.txt')
with open(filepath, 'w') as output:
output.write(raptor.device.shell_output("dumpsys battery"))
def finish_geckoview_power_test(raptor):
upload_dir = os.getenv('MOZ_UPLOAD_DIR')
if not upload_dir:
raptor.log.critical("Geckoview power test ignored because MOZ_UPLOAD_DIR was not set")
return
# Restore the screen off timeout.
raptor.device.shell_output(
"settings put system screen_off_timeout %s" % raptor.screen_off_timeout)
filepath = os.path.join(upload_dir, 'battery-after.txt')
with open(filepath, 'w') as output:
output.write(raptor.device.shell_output("dumpsys battery"))
verbose = raptor.device._verbose
raptor.device._verbose = False
filepath = os.path.join(upload_dir, 'batterystats.csv')
with open(filepath, 'w') as output:
output.write(raptor.device.shell_output("dumpsys batterystats --checkin"))
filepath = os.path.join(upload_dir, 'batterystats.txt')
with open(filepath, 'w') as output:
batterystats = raptor.device.shell_output("dumpsys batterystats")
output.write(batterystats)
raptor.device._verbose = verbose
uid = None
cpu = wifi = smearing = screen = proportional = 0
r_uid = re.compile(r'proc=([^:]+):"%s"' % raptor.config['binary'])
batterystats = batterystats.split('\n')
for line in batterystats:
if uid is None:
match = r_uid.search(line)
if match:
uid = match.group(1)
r_power = re.compile(
r'\s+Uid %s:\s+[\d.]+ [(] cpu=([\d.]+) wifi=([\d.]+) [)] '
r'Including smearing: ([\d.]+)' % uid)
else:
match = r_power.match(line)
if match:
(cpu, wifi, smearing) = match.groups()
r_screen = re.compile(r'screen=([\d.]+)')
match = r_screen.search(line)
if match:
screen = match.group(1)
r_proportional = re.compile(r'proportional=([\d.]+)')
match = r_proportional.search(line)
if match:
proportional = match.group(1)
break
raptor.log.info('uid: %s, cpu: %s, wifi: %s, screen: %s, proportional: %s' %
(uid, cpu, wifi, screen, proportional))
raptor.device.command_output(["bugreport", upload_dir])

View File

@ -46,6 +46,7 @@ from manifest import get_raptor_test_list
from playback import get_playback
from results import RaptorResultsHandler
from gecko_profile import GeckoProfile
from power import init_geckoview_power_test, finish_geckoview_power_test
class Raptor(object):
@ -53,7 +54,8 @@ class Raptor(object):
def __init__(self, app, binary, run_local=False, obj_path=None,
gecko_profile=False, gecko_profile_interval=None, gecko_profile_entries=None,
symbols_path=None, host=None, is_release_build=False, debug_mode=False):
symbols_path=None, host=None, power_test=False, is_release_build=False,
debug_mode=False):
self.config = {}
self.config['app'] = app
self.config['binary'] = binary
@ -66,6 +68,7 @@ class Raptor(object):
self.config['gecko_profile_entries'] = gecko_profile_entries
self.config['symbols_path'] = symbols_path
self.config['host'] = host
self.config['power_test'] = power_test
self.config['is_release_build'] = is_release_build
self.raptor_venv = os.path.join(os.getcwd(), 'raptor-venv')
self.log = get_default_logger(component='raptor-main')
@ -114,6 +117,8 @@ class Raptor(object):
self.log.info("creating android device handler using mozdevice")
self.device = ADBDevice(verbose=True)
self.device.clear_logcat()
if self.config['power_test']:
init_geckoview_power_test(self)
else:
# create the desktop browser runner
self.log.info("creating browser runner using mozrunner")
@ -290,6 +295,8 @@ class Raptor(object):
fail_if_running=False)
except Exception:
self.log.error("Exception launching %s" % self.config['binary'])
if self.config['power_test']:
finish_geckoview_power_test(self)
raise
self.control_server.device = self.device
self.control_server.app_name = self.config['binary']
@ -375,12 +382,15 @@ class Raptor(object):
self.control_server.wait_for_quit()
break
finally:
if self.config['app'] != "geckoview":
if self.config['app'] == "geckoview":
# TODO: if on geckoview is there some cleanup here i.e. check for crashes?
if self.config['power_test']:
finish_geckoview_power_test(self)
else:
try:
self.runner.check_for_crashes()
except NotImplementedError: # not implemented for Chrome
pass
# TODO: if on geckoview is there some cleanup here i.e. check for crashes?
if self.playback is not None:
self.playback.stop()
@ -543,6 +553,7 @@ def main(args=sys.argv[1:]):
gecko_profile_entries=args.gecko_profile_entries,
symbols_path=args.symbols_path,
host=args.host,
power_test=args.power_test,
is_release_build=args.is_release_build,
debug_mode=args.debug_mode)