mirror of
https://github.com/ARM-software/workload-automation.git
synced 2025-01-19 12:24:32 +00:00
3043506d86
applaunch creates and deploys an auxilary script in order to collect precise timings. This script invoked busybox with the assumption that it is in PATH. Since recent changes mean that it is no longer deployed to /system/bin, the busybox in not found. With this commit, the full path to busybox will be passed into the script's template.
190 lines
8.2 KiB
Python
190 lines
8.2 KiB
Python
# Copyright 2013-2015 ARM Limited
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
|
|
# pylint: disable=E1101
|
|
|
|
from __future__ import division
|
|
import os
|
|
|
|
try:
|
|
import jinja2
|
|
except ImportError:
|
|
jinja2 = None
|
|
|
|
from wlauto import Workload, settings, Parameter
|
|
from wlauto.exceptions import WorkloadError
|
|
from wlauto.utils.hwmon import discover_sensors
|
|
from wlauto.utils.misc import get_meansd
|
|
from wlauto.utils.types import boolean, identifier, list_of_strs
|
|
|
|
|
|
THIS_DIR = os.path.dirname(__file__)
|
|
TEMPLATE_NAME = 'device_script.template'
|
|
SCRIPT_TEMPLATE = os.path.join(THIS_DIR, TEMPLATE_NAME)
|
|
|
|
APP_CONFIG = {
|
|
'browser': {
|
|
'package': 'com.android.browser',
|
|
'activity': '.BrowserActivity',
|
|
'options': '-d about:blank',
|
|
},
|
|
'calculator': {
|
|
'package': 'com.android.calculator2',
|
|
'activity': '.Calculator',
|
|
'options': '',
|
|
},
|
|
'calendar': {
|
|
'package': 'com.android.calendar',
|
|
'activity': '.LaunchActivity',
|
|
'options': '',
|
|
},
|
|
}
|
|
|
|
|
|
class ApplaunchWorkload(Workload):
|
|
|
|
name = 'applaunch'
|
|
description = """
|
|
Measures the time and energy used in launching an application.
|
|
|
|
"""
|
|
supported_platforms = ['android']
|
|
|
|
parameters = [
|
|
Parameter('app', default='browser', allowed_values=['calculator', 'browser', 'calendar'],
|
|
description='The name of the application to measure.'),
|
|
Parameter('set_launcher_affinity', kind=bool, default=True,
|
|
description=('If ``True``, this will explicitly set the affinity of the launcher '
|
|
'process to the A15 cluster.')),
|
|
Parameter('times', kind=int, default=8,
|
|
description='Number of app launches to do on the device.'),
|
|
Parameter('measure_energy', kind=boolean, default=False,
|
|
description="""
|
|
Specfies wether energy measurments should be taken during the run.
|
|
|
|
.. note:: This depends on appropriate sensors to be exposed through HWMON.
|
|
|
|
"""),
|
|
Parameter('io_stress', kind=boolean, default=False,
|
|
description='Specifies whether to stress IO during App launch.'),
|
|
Parameter('io_scheduler', allowed_values=['noop', 'deadline', 'row', 'cfq', 'bfq'],
|
|
description='Set the IO scheduler to test on the device.'),
|
|
Parameter('cleanup', kind=boolean, default=True,
|
|
description='Specifies whether to clean up temporary files on the device.'),
|
|
]
|
|
|
|
def __init__(self, device, **kwargs):
|
|
super(ApplaunchWorkload, self).__init__(device, **kwargs)
|
|
if not jinja2:
|
|
raise WorkloadError('Please install jinja2 Python package: "sudo pip install jinja2"')
|
|
filename = '{}-{}.sh'.format(self.name, self.app)
|
|
self.host_script_file = os.path.join(settings.meta_directory, filename)
|
|
self.device_script_file = os.path.join(self.device.working_directory, filename)
|
|
self._launcher_pid = None
|
|
self._old_launcher_affinity = None
|
|
self.sensors = []
|
|
|
|
def on_run_init(self, context): # pylint: disable=W0613
|
|
if self.measure_energy:
|
|
self.sensors = discover_sensors(self.device, ['energy'])
|
|
for sensor in self.sensors:
|
|
sensor.label = identifier(sensor.label).upper()
|
|
|
|
def setup(self, context):
|
|
self.logger.debug('Creating script {}'.format(self.host_script_file))
|
|
with open(self.host_script_file, 'w') as wfh:
|
|
env = jinja2.Environment(loader=jinja2.FileSystemLoader(THIS_DIR))
|
|
template = env.get_template(TEMPLATE_NAME)
|
|
wfh.write(template.render(device=self.device, # pylint: disable=maybe-no-member
|
|
sensors=self.sensors,
|
|
iterations=self.times,
|
|
io_stress=self.io_stress,
|
|
io_scheduler=self.io_scheduler,
|
|
cleanup=self.cleanup,
|
|
package=APP_CONFIG[self.app]['package'],
|
|
activity=APP_CONFIG[self.app]['activity'],
|
|
options=APP_CONFIG[self.app]['options'],
|
|
busybox=self.device.busybox,
|
|
))
|
|
self.device_script_file = self.device.install(self.host_script_file)
|
|
if self.set_launcher_affinity:
|
|
self._set_launcher_affinity()
|
|
self.device.clear_logcat()
|
|
|
|
def run(self, context):
|
|
self.device.execute('sh {}'.format(self.device_script_file), timeout=300, as_root=self.io_stress)
|
|
|
|
def update_result(self, context): # pylint: disable=too-many-locals
|
|
result_files = ['time.result']
|
|
result_files += ['{}.result'.format(sensor.label) for sensor in self.sensors]
|
|
metric_suffix = ''
|
|
if self.io_stress:
|
|
host_scheduler_file = os.path.join(context.output_directory, 'scheduler')
|
|
device_scheduler_file = '/sys/block/mmcblk0/queue/scheduler'
|
|
self.device.pull_file(device_scheduler_file, host_scheduler_file)
|
|
with open(host_scheduler_file) as fh:
|
|
scheduler = fh.read()
|
|
scheduler_used = scheduler[scheduler.index("[") + 1:scheduler.index("]")]
|
|
metric_suffix = '_' + scheduler_used
|
|
for filename in result_files:
|
|
self._extract_results_from_file(context, filename, metric_suffix)
|
|
|
|
def teardown(self, context):
|
|
if self.set_launcher_affinity:
|
|
self._reset_launcher_affinity()
|
|
if self.cleanup:
|
|
self.device.delete_file(self.device_script_file)
|
|
|
|
def _set_launcher_affinity(self):
|
|
try:
|
|
self._launcher_pid = self.device.get_pids_of('com.android.launcher')[0]
|
|
result = self.device.execute('taskset -p {}'.format(self._launcher_pid), busybox=True, as_root=True)
|
|
self._old_launcher_affinity = int(result.split(':')[1].strip(), 16)
|
|
|
|
cpu_ids = [i for i, x in enumerate(self.device.core_names) if x == 'a15']
|
|
if not cpu_ids or len(cpu_ids) == len(self.device.core_names):
|
|
self.logger.debug('Cannot set affinity.')
|
|
return
|
|
|
|
new_mask = reduce(lambda x, y: x | y, cpu_ids, 0x0)
|
|
self.device.execute('taskset -p 0x{:X} {}'.format(new_mask, self._launcher_pid), busybox=True, as_root=True)
|
|
except IndexError:
|
|
raise WorkloadError('Could not set affinity of launcher: PID not found.')
|
|
|
|
def _reset_launcher_affinity(self):
|
|
command = 'taskset -p 0x{:X} {}'.format(self._old_launcher_affinity, self._launcher_pid)
|
|
self.device.execute(command, busybox=True, as_root=True)
|
|
|
|
def _extract_results_from_file(self, context, filename, metric_suffix):
|
|
host_result_file = os.path.join(context.output_directory, filename)
|
|
device_result_file = self.device.path.join(self.device.working_directory, filename)
|
|
self.device.pull_file(device_result_file, host_result_file)
|
|
|
|
with open(host_result_file) as fh:
|
|
if filename == 'time.result':
|
|
values = [v / 1000 for v in map(int, fh.read().split())]
|
|
_add_metric(context, 'time' + metric_suffix, values, 'Seconds')
|
|
else:
|
|
metric = filename.replace('.result', '').lower()
|
|
numbers = iter(map(int, fh.read().split()))
|
|
deltas = [(after - before) / 1000000 for before, after in zip(numbers, numbers)]
|
|
_add_metric(context, metric, deltas, 'Joules')
|
|
|
|
|
|
def _add_metric(context, metric, values, units):
|
|
mean, sd = get_meansd(values)
|
|
context.result.add_metric(metric, mean, units)
|
|
context.result.add_metric(metric + ' sd', sd, units, lower_is_better=True)
|