diff --git a/wlauto/common/android/device.py b/wlauto/common/android/device.py index 682a52a3..3f34cc86 100644 --- a/wlauto/common/android/device.py +++ b/wlauto/common/android/device.py @@ -51,7 +51,7 @@ class AndroidDevice(BaseLinuxDevice): # pylint: disable=W0223 description='The format of matching the shell prompt in Android.'), Parameter('working_directory', default='/sdcard/wa-working', description='Directory that will be used WA on the device for output files etc.'), - Parameter('binaries_directory', default='/system/bin', + Parameter('binaries_directory', default='/data/local/tmp', override=True, description='Location of binaries on the device.'), Parameter('package_data_directory', default='/data/data', description='Location of of data for an installed package (APK).'), @@ -78,6 +78,8 @@ class AndroidDevice(BaseLinuxDevice): # pylint: disable=W0223 If set a swipe of the specified direction will be performed. This should unlock the screen. """), + Parameter('binaries_directory', default="/data/local/tmp", override=True, + description='Location of executable binaries on this device (must be in PATH).'), ] default_timeout = 30 @@ -193,9 +195,7 @@ class AndroidDevice(BaseLinuxDevice): # pylint: disable=W0223 self._is_ready = True def initialize(self, context): - self.execute('mkdir -p {}'.format(self.working_directory)) if self.is_rooted: - self.busybox = self.deploy_busybox(context) self.disable_screen_lock() self.disable_selinux() if self.enable_screen_check: @@ -281,11 +281,24 @@ class AndroidDevice(BaseLinuxDevice): # pylint: disable=W0223 """ return package_name in self.list_packages() - def executable_is_installed(self, executable_name): - return executable_name in self.listdir(self.binaries_directory) + def executable_is_installed(self, executable_name): # pylint: disable=unused-argument,no-self-use + raise AttributeError("""Instead of using is_installed, please use + ``get_binary_path`` or ``install_if_needed`` instead. You should + use the path returned by these functions to then invoke the binary + + please see: https://pythonhosted.org/wlauto/writing_extensions.html""") def is_installed(self, name): - return self.executable_is_installed(name) or self.package_is_installed(name) + if self.package_is_installed(name): + return True + elif "." in name: # assumes android packages have a . in their name and binaries documentation + return False + else: + raise AttributeError("""Instead of using is_installed, please use + ``get_binary_path`` or ``install_if_needed`` instead. You should + use the path returned by these functions to then invoke the binary + + please see: https://pythonhosted.org/wlauto/writing_extensions.html""") def listdir(self, path, as_root=False, **kwargs): contents = self.execute('ls {}'.format(path), as_root=as_root) @@ -352,7 +365,7 @@ class AndroidDevice(BaseLinuxDevice): # pylint: disable=W0223 def install_executable(self, filepath, with_name=None): """ - Installs a binary executable on device. Requires root access. Returns + Installs a binary executable on device. Returns the path to the installed binary, or ``None`` if the installation has failed. Optionally, ``with_name`` parameter may be used to specify a different name under which the executable will be installed. @@ -376,12 +389,13 @@ class AndroidDevice(BaseLinuxDevice): # pylint: disable=W0223 def uninstall_executable(self, executable_name): """ - Requires root access. Added in version 2.1.3. """ - on_device_executable = self.path.join(self.binaries_directory, executable_name) + on_device_executable = self.get_binary_path(executable_name, search_system_binaries=False) + if not on_device_executable: + raise DeviceError("Could not uninstall {}, binary not found".format(on_device_executable)) self._ensure_binaries_directory_is_writable() self.delete_file(on_device_executable, as_root=self.is_rooted) @@ -405,7 +419,7 @@ class AndroidDevice(BaseLinuxDevice): # pylint: disable=W0223 Added in version 2.1.3 - .. note:: The device must be rooted to be able to use busybox. + .. note:: The device must be rooted to be able to use some busybox features. :param as_root: If ``True``, will attempt to execute command in privileged mode. The device must be rooted, otherwise an error will be raised. Defaults to ``False``. @@ -424,9 +438,6 @@ class AndroidDevice(BaseLinuxDevice): # pylint: disable=W0223 if as_root and not self.is_rooted: raise DeviceError('Attempting to execute "{}" as root on unrooted device.'.format(command)) if busybox: - if not self.is_rooted: - DeviceError('Attempting to execute "{}" with busybox. '.format(command) + - 'Busybox can only be deployed to rooted devices.') command = ' '.join([self.busybox, command]) if background: return adb_background_shell(self.adb_name, command, as_root=as_root) diff --git a/wlauto/common/linux/device.py b/wlauto/common/linux/device.py index a3957171..6d59de1f 100644 --- a/wlauto/common/linux/device.py +++ b/wlauto/common/linux/device.py @@ -89,6 +89,8 @@ class BaseLinuxDevice(Device): # pylint: disable=abstract-method These paths do not have to exist and will be ignored if the path is not present on a particular device. '''), + Parameter('binaries_directory', + description='Location of executable binaries on this device (must be in PATH).'), ] @@ -183,11 +185,14 @@ class BaseLinuxDevice(Device): # pylint: disable=abstract-method def initialize(self, context): self.execute('mkdir -p {}'.format(self.working_directory)) - if self.is_rooted: - if not self.is_installed('busybox'): - self.busybox = self.deploy_busybox(context) - else: - self.busybox = 'busybox' + if not self.binaries_directory: + self._set_binaries_dir() + self.execute('mkdir -p {}'.format(self.binaries_directory)) + self.busybox = self.deploy_busybox(context) + + def _set_binaries_dir(self): + # pylint: disable=attribute-defined-outside-init + self.binaries_directory = self.path.join(self.working_directory, "bin") def is_file(self, filepath): output = self.execute('if [ -f \'{}\' ]; then echo 1; else echo 0; fi'.format(filepath)) @@ -281,7 +286,6 @@ class BaseLinuxDevice(Device): # pylint: disable=abstract-method Deploys the busybox binary to the specified device and returns the path to the binary on the device. - :param device: device to deploy the binary to. :param context: an instance of ExecutionContext :param force: by default, if the binary is already present on the device, it will not be deployed again. Setting force @@ -291,11 +295,61 @@ class BaseLinuxDevice(Device): # pylint: disable=abstract-method :returns: The on-device path to the busybox binary. """ - on_device_executable = self.path.join(self.binaries_directory, 'busybox') - if not force and self.file_exists(on_device_executable): - return on_device_executable - host_file = context.resolver.get(Executable(NO_ONE, self.abi, 'busybox')) - return self.install(host_file) + on_device_executable = self.get_binary_path("busybox", search_system_binaries=False) + if force or not on_device_executable: + host_file = context.resolver.get(Executable(NO_ONE, self.abi, 'busybox')) + return self.install(host_file) + return on_device_executable + + def is_installed(self, name): # pylint: disable=unused-argument,no-self-use + raise AttributeError("""Instead of using is_installed, please use + ``get_binary_path`` or ``install_if_needed`` instead. You should + use the path returned by these functions to then invoke the binary + + please see: https://pythonhosted.org/wlauto/writing_extensions.html""") + + def get_binary_path(self, name, search_system_binaries=True): + """ + Searches the devices ``binary_directory`` for the given binary, + if it cant find it there it tries using which to find it. + + :param name: The name of the binary + :param search_system_binaries: By default this function will try using + which to find the binary if it isn't in + ``binary_directory``. When this is set + to ``False`` it will not try this. + + :returns: The on-device path to the binary. + + """ + wa_binary = self.path.join(self.binaries_directory, name) + if self.file_exists(wa_binary): + return wa_binary + if search_system_binaries: + try: + return self.execute('{} which {}'.format(self.busybox, name)).strip() + except DeviceError: + pass + return None + + def install_if_needed(self, host_path, search_system_binaries=True): + """ + Similar to get_binary_path but will install the binary if not found. + + :param host_path: The path to the binary on the host + :param search_system_binaries: By default this function will try using + which to find the binary if it isn't in + ``binary_directory``. When this is set + to ``False`` it will not try this. + + :returns: The on-device path to the binary. + + """ + binary_path = self.get_binary_path(os.path.split(host_path)[1], + search_system_binaries=search_system_binaries) + if not binary_path: + binary_path = self.install(host_path) + return binary_path def list_file_systems(self): output = self.execute('mount') @@ -527,8 +581,6 @@ class LinuxDevice(BaseLinuxDevice): has write permissions. This will default to /home/<username>/wa (or to /root/wa, if username is 'root'). '''), - Parameter('binaries_directory', default='/usr/local/bin', - description='Location of executable binaries on this device (must be in PATH).'), ] @property @@ -554,7 +606,6 @@ class LinuxDevice(BaseLinuxDevice): def __init__(self, *args, **kwargs): super(LinuxDevice, self).__init__(*args, **kwargs) self.shell = None - self.local_binaries_directory = None self._is_rooted = None def validate(self): @@ -563,12 +614,9 @@ class LinuxDevice(BaseLinuxDevice): self.working_directory = '/root/wa' # pylint: disable=attribute-defined-outside-init else: self.working_directory = '/home/{}/wa'.format(self.username) # pylint: disable=attribute-defined-outside-init - self.local_binaries_directory = self.path.join(self.working_directory, 'bin') def initialize(self, context, *args, **kwargs): - self.execute('mkdir -p {}'.format(self.local_binaries_directory)) self.execute('mkdir -p {}'.format(self.binaries_directory)) - self.execute('export PATH={}:$PATH'.format(self.local_binaries_directory)) self.execute('export PATH={}:$PATH'.format(self.binaries_directory)) super(LinuxDevice, self).initialize(context, *args, **kwargs) @@ -747,37 +795,22 @@ class LinuxDevice(BaseLinuxDevice): return [x.strip() for x in contents.split('\n')] # pylint: disable=maybe-no-member def install(self, filepath, timeout=default_timeout, with_name=None): # pylint: disable=W0221 - if self.is_rooted: - destpath = self.path.join(self.binaries_directory, - with_name and with_name or self.path.basename(filepath)) - self.push_file(filepath, destpath, as_root=True) - self.execute('chmod a+x {}'.format(destpath), timeout=timeout, as_root=True) - else: - destpath = self.path.join(self.local_binaries_directory, - with_name and with_name or self.path.basename(filepath)) - self.push_file(filepath, destpath) - self.execute('chmod a+x {}'.format(destpath), timeout=timeout) + destpath = self.path.join(self.binaries_directory, + with_name or self.path.basename(filepath)) + self.push_file(filepath, destpath, as_root=True) + self.execute('chmod a+x {}'.format(destpath), timeout=timeout, as_root=True) return destpath install_executable = install # compatibility - def uninstall(self, name): - if self.is_rooted: - path = self.path.join(self.binaries_directory, name) - self.delete_file(path, as_root=True) - else: - path = self.path.join(self.local_binaries_directory, name) - self.delete_file(path) + def uninstall(self, executable_name): + on_device_executable = self.get_binary_path(executable_name, search_system_binaries=False) + if not on_device_executable: + raise DeviceError("Could not uninstall {}, binary not found".format(on_device_executable)) + self.delete_file(on_device_executable, as_root=self.is_rooted) uninstall_executable = uninstall # compatibility - def is_installed(self, name): - try: - self.execute('which {}'.format(name)) - return True - except DeviceError: - return False - # misc def ping(self): @@ -788,7 +821,7 @@ class LinuxDevice(BaseLinuxDevice): raise DeviceNotRespondingError(self.host) def capture_screen(self, filepath): - if not self.is_installed('scrot'): + if not self.get_binary_path('scrot'): self.logger.debug('Could not take screenshot as scrot is not installed.') return try: diff --git a/wlauto/instrumentation/perf/__init__.py b/wlauto/instrumentation/perf/__init__.py index 90da6ad9..50be7376 100644 --- a/wlauto/instrumentation/perf/__init__.py +++ b/wlauto/instrumentation/perf/__init__.py @@ -91,11 +91,11 @@ class PerfInstrument(Instrument): ] def on_run_init(self, context): - if not self.device.is_installed('perf') or self.force_install: - binary = context.resolver.get(Executable(self, self.device.abi, 'perf')) + binary = context.resolver.get(Executable(self, self.device.abi, 'perf')) + if self.force_install: self.binary = self.device.install(binary) else: - self.binary = 'perf' + self.binary = self.device.install_if_needed(binary) self.commands = self._build_commands() def setup(self, context): diff --git a/wlauto/instrumentation/trace_cmd/__init__.py b/wlauto/instrumentation/trace_cmd/__init__.py index d417bd00..babbabcc 100644 --- a/wlauto/instrumentation/trace_cmd/__init__.py +++ b/wlauto/instrumentation/trace_cmd/__init__.py @@ -164,11 +164,12 @@ class TraceCmdInstrument(Instrument): raise InstrumentError('trace-cmd instrument cannot be used on an unrooted device.') if not self.no_install: host_file = context.resolver.get(Executable(self, self.device.abi, 'trace-cmd')) - self.trace_cmd = self.device.install_executable(host_file) + self.trace_cmd = self.device.install(host_file) else: - if not self.device.is_installed('trace-cmd'): + self.trace_cmd = self.device.get_binary_path("trace-cmd") + if not self.trace_cmd: raise ConfigError('No trace-cmd found on device and no_install=True is specified.') - self.trace_cmd = 'trace-cmd' + # Register ourselves as absolute last event before and # first after so we can mark the trace at the right time signal.connect(self.insert_start_mark, signal.BEFORE_WORKLOAD_EXECUTION, priority=11) diff --git a/wlauto/workloads/cyclictest/__init__.py b/wlauto/workloads/cyclictest/__init__.py index 700bd993..c7d2e36e 100644 --- a/wlauto/workloads/cyclictest/__init__.py +++ b/wlauto/workloads/cyclictest/__init__.py @@ -79,11 +79,8 @@ class Cyclictest(Workload): if not self.device.is_rooted: raise WorkloadError("This workload requires a device with root premissions to run") - if not self.device.is_installed('cyclictest'): - host_binary = context.resolver.get(Executable(self, self.device.abi, 'cyclictest')) - self.device_binary = self.device.install(host_binary) - else: - self.device_binary = 'cyclictest' + host_binary = context.resolver.get(Executable(self, self.device.abi, 'cyclictest')) + self.device_binary = self.device.install(host_binary) self.cyclictest_command = self.cyclictest_command.format(self.device_binary, 0 if self.clock == 'monotonic' else 1, diff --git a/wlauto/workloads/ebizzy/__init__.py b/wlauto/workloads/ebizzy/__init__.py index 2aabfcfa..366ce96b 100644 --- a/wlauto/workloads/ebizzy/__init__.py +++ b/wlauto/workloads/ebizzy/__init__.py @@ -57,11 +57,8 @@ class Ebizzy(Workload): self.run_timeout = self.seconds + timeout_buf self.binary_name = 'ebizzy' - if not self.device.is_installed(self.binary_name): - host_binary = context.resolver.get(Executable(self, self.device.abi, self.binary_name)) - self.device_binary = self.device.install(host_binary) - else: - self.device_binary = self.binary_name + host_binary = context.resolver.get(Executable(self, self.device.abi, self.binary_name)) + self.device_binary = self.device.install_if_needed(host_binary) self.command = self.command.format(self.device_binary, self.threads, self.seconds, self.chunks, self.extra_params, self.ebizzy_results) diff --git a/wlauto/workloads/hackbench/__init__.py b/wlauto/workloads/hackbench/__init__.py index dbb294e7..32d3b997 100644 --- a/wlauto/workloads/hackbench/__init__.py +++ b/wlauto/workloads/hackbench/__init__.py @@ -61,11 +61,8 @@ class Hackbench(Workload): self.run_timeout = self.duration + timeout_buf self.binary_name = 'hackbench' - if not self.device.is_installed(self.binary_name): - host_binary = context.resolver.get(Executable(self, self.device.abi, self.binary_name)) - self.device_binary = self.device.install(host_binary) - else: - self.device_binary = self.binary_name + host_binary = context.resolver.get(Executable(self, self.device.abi, self.binary_name)) + self.device_binary = self.device.install(host_binary) self.command = self.command.format(self.device_binary, self.datasize, self.groups, self.loops, self.extra_params, self.hackbench_result) diff --git a/wlauto/workloads/memcpy/__init__.py b/wlauto/workloads/memcpy/__init__.py index 54508363..81249b31 100644 --- a/wlauto/workloads/memcpy/__init__.py +++ b/wlauto/workloads/memcpy/__init__.py @@ -18,7 +18,7 @@ import os import re -from wlauto import Workload, Parameter +from wlauto import Workload, Parameter, Executable THIS_DIR = os.path.dirname(__file__) @@ -54,11 +54,10 @@ class MemcpyTest(Workload): ] def setup(self, context): - self.host_binary = os.path.join(THIS_DIR, 'memcpy') - if not self.device.is_installed('memcpy'): - self.device_binary = self.device.install(self.host_binary) - else: - self.device_binary = 'memcpy' + self.binary_name = 'memcpy' + host_binary = context.resolver.get(Executable(self, self.device.abi, self.binary_name)) + self.device_binary = self.device.install_if_needed(host_binary) + self.command = '{} -i {} -s {}'.format(self.device_binary, self.iterations, self.buffer_size) if self.cpus: for c in self.cpus: diff --git a/wlauto/workloads/memcpy/bin/arm64/memcpy b/wlauto/workloads/memcpy/bin/arm64/memcpy new file mode 100755 index 00000000..39982df8 Binary files /dev/null and b/wlauto/workloads/memcpy/bin/arm64/memcpy differ diff --git a/wlauto/workloads/memcpy/memcpy b/wlauto/workloads/memcpy/bin/armeabi/memcpy similarity index 100% rename from wlauto/workloads/memcpy/memcpy rename to wlauto/workloads/memcpy/bin/armeabi/memcpy diff --git a/wlauto/workloads/rt_app/__init__.py b/wlauto/workloads/rt_app/__init__.py index 6d1407c8..ce8003dc 100644 --- a/wlauto/workloads/rt_app/__init__.py +++ b/wlauto/workloads/rt_app/__init__.py @@ -216,14 +216,13 @@ class RtApp(Workload): def _deploy_rt_app_binary_if_necessary(self): # called from initialize() so gets invoked once per run - if self.force_install or not self.device.is_installed(BINARY_NAME): + RtApp.device_binary = self.device.get_binary_path("rt-app") + if self.force_install or not RtApp.device_binary: if not self.host_binary: message = '''rt-app is not installed on the device and could not be found in workload resources''' raise ResourceError(message) RtApp.device_binary = self.device.install(self.host_binary) - else: - RtApp.device_binary = BINARY_NAME def _load_json_config(self, context): user_config_file = self._get_raw_json_config(context.resolver) @@ -280,4 +279,3 @@ class RtApp(Workload): tf.extractall(context.output_directory) os.remove(host_path) self.device.execute('rm -rf {}/*'.format(self.device_working_directory)) - diff --git a/wlauto/workloads/sysbench/__init__.py b/wlauto/workloads/sysbench/__init__.py index a241f1b5..2787911a 100644 --- a/wlauto/workloads/sysbench/__init__.py +++ b/wlauto/workloads/sysbench/__init__.py @@ -132,13 +132,13 @@ class Sysbench(Workload): self.device.delete_file(self.results_file) def _check_executable(self): - self.on_device_binary = self.device.path.join(self.device.binaries_directory, 'sysbench') - if self.device.is_installed('sysbench') and not self.force_install: - self.logger.debug('sysbench found on device') - return - if not self.on_host_binary: + self.on_device_binary = self.device.get_binary_path("sysbench") + if not self.on_device_binary and not self.on_host_binary: raise WorkloadError('sysbench binary is not installed on the device, and it is not found on the host.') - self.device.install(self.on_host_binary) + if self.force_install: + self.device.install(self.on_host_binary) + else: + self.device.install_if_needed(self.on_host_binary) def _build_command(self, **parameters): param_strings = ['--{}={}'.format(k.replace('_', '-'), v)