diff --git a/devlib/__init__.py b/devlib/__init__.py index f4b3ac4..d290b1f 100644 --- a/devlib/__init__.py +++ b/devlib/__init__.py @@ -15,7 +15,7 @@ from devlib.target import Target, LinuxTarget, AndroidTarget, LocalLinuxTarget, ChromeOsTarget from devlib.host import PACKAGE_BIN_DIRECTORY -from devlib.exception import DevlibError, TargetError, HostError, TargetNotRespondingError +from devlib.exception import DevlibError, DevlibTransientError, DevlibStableError, TargetError, TargetTransientError, TargetStableError, TargetNotRespondingError, HostError from devlib.module import Module, HardRestModule, BootModule, FlashModule from devlib.module import get_module, register_module diff --git a/devlib/exception.py b/devlib/exception.py index 9a54c4a..0004a4b 100644 --- a/devlib/exception.py +++ b/devlib/exception.py @@ -22,12 +22,43 @@ class DevlibError(Exception): return str(self) +class DevlibStableError(DevlibError): + """Non transient target errors, that are not subject to random variations + in the environment and can be reliably linked to for example a missing + feature on a target.""" + pass + + +class DevlibTransientError(DevlibError): + """Exceptions inheriting from ``DevlibTransientError`` represent random + transient events that are usually related to issues in the environment, as + opposed to programming errors, for example network failures or + timeout-related exceptions. When the error could come from + indistinguishable transient or non-transient issue, it can generally be + assumed that the configuration is correct and therefore, a transient + exception is raised.""" + pass + + class TargetError(DevlibError): """An error has occured on the target""" pass -class TargetNotRespondingError(DevlibError): +class TargetTransientError(TargetError, DevlibTransientError): + """Transient target errors that can happen randomly when everything is + properly configured.""" + pass + + +class TargetStableError(TargetError, DevlibStableError): + """Non-transient target errors that can be linked to a programming error or + a configuration issue, and is not influenced by non-controllable parameters + such as network issues.""" + pass + + +class TargetNotRespondingError(TargetTransientError): """The target is unresponsive.""" pass @@ -38,7 +69,7 @@ class HostError(DevlibError): # pylint: disable=redefined-builtin -class TimeoutError(DevlibError): +class TimeoutError(DevlibTransientError): """Raised when a subprocess command times out. This is basically a ``DevlibError``-derived version of ``subprocess.CalledProcessError``, the thinking being that while a timeout could be due to programming error (e.g. not setting long enough timers), it is often due to some failure in the diff --git a/devlib/host.py b/devlib/host.py index df3ccc1..f68173c 100644 --- a/devlib/host.py +++ b/devlib/host.py @@ -20,7 +20,7 @@ import subprocess import logging from getpass import getpass -from devlib.exception import TargetError +from devlib.exception import TargetTransientError, TargetStableError from devlib.utils.misc import check_output PACKAGE_BIN_DIRECTORY = os.path.join(os.path.dirname(__file__), 'bin') @@ -59,11 +59,11 @@ class LocalConnection(object): # pylint: disable=unused-argument def execute(self, command, timeout=None, check_exit_code=True, - as_root=False, strip_colors=True): + as_root=False, strip_colors=True, will_succeed=False): self.logger.debug(command) if as_root: if self.unrooted: - raise TargetError('unrooted') + raise TargetStableError('unrooted') password = self._get_password() command = 'echo \'{}\' | sudo -S '.format(password) + command ignore = None if check_exit_code else 'all' @@ -72,12 +72,15 @@ class LocalConnection(object): except subprocess.CalledProcessError as e: message = 'Got exit code {}\nfrom: {}\nOUTPUT: {}'.format( e.returncode, command, e.output) - raise TargetError(message) + if will_succeed: + raise TargetTransientError(message) + else: + raise TargetStableError(message) def background(self, command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, as_root=False): if as_root: if self.unrooted: - raise TargetError('unrooted') + raise TargetStableError('unrooted') password = self._get_password() command = 'echo \'{}\' | sudo -S '.format(password) + command return subprocess.Popen(command, stdout=stdout, stderr=stderr, shell=True) diff --git a/devlib/instrument/gem5power.py b/devlib/instrument/gem5power.py index 4ae901a..35b338b 100644 --- a/devlib/instrument/gem5power.py +++ b/devlib/instrument/gem5power.py @@ -16,7 +16,7 @@ from __future__ import division from devlib.platform.gem5 import Gem5SimulationPlatform from devlib.instrument import Instrument, CONTINUOUS, MeasurementsCsv -from devlib.exception import TargetError +from devlib.exception import TargetStableError from devlib.utils.csvutil import csvwriter @@ -36,9 +36,9 @@ class Gem5PowerInstrument(Instrument): system.cluster0.cores0.power_model.static_power ''' if not isinstance(target.platform, Gem5SimulationPlatform): - raise TargetError('Gem5PowerInstrument requires a gem5 platform') + raise TargetStableError('Gem5PowerInstrument requires a gem5 platform') if not target.has('gem5stats'): - raise TargetError('Gem5StatsModule is not loaded') + raise TargetStableError('Gem5StatsModule is not loaded') super(Gem5PowerInstrument, self).__init__(target) # power_sites is assumed to be a list later diff --git a/devlib/instrument/hwmon.py b/devlib/instrument/hwmon.py index 72fb2ec..8c7f15d 100644 --- a/devlib/instrument/hwmon.py +++ b/devlib/instrument/hwmon.py @@ -16,7 +16,7 @@ from __future__ import division import re from devlib.instrument import Instrument, Measurement, INSTANTANEOUS -from devlib.exception import TargetError +from devlib.exception import TargetStableError class HwmonInstrument(Instrument): @@ -35,7 +35,7 @@ class HwmonInstrument(Instrument): def __init__(self, target): if not hasattr(target, 'hwmon'): - raise TargetError('Target does not support HWMON') + raise TargetStableError('Target does not support HWMON') super(HwmonInstrument, self).__init__(target) self.logger.debug('Discovering available HWMON sensors...') diff --git a/devlib/instrument/netstats/__init__.py b/devlib/instrument/netstats/__init__.py index 797d4a7..8203ceb 100644 --- a/devlib/instrument/netstats/__init__.py +++ b/devlib/instrument/netstats/__init__.py @@ -22,7 +22,7 @@ from collections import defaultdict from future.moves.itertools import zip_longest from devlib.instrument import Instrument, MeasurementsCsv, CONTINUOUS -from devlib.exception import TargetError, HostError +from devlib.exception import TargetStableError, HostError from devlib.utils.android import ApkInfo from devlib.utils.csvutil import csvwriter @@ -84,7 +84,7 @@ class NetstatsInstrument(Instrument): """ if target.os != 'android': - raise TargetError('netstats insturment only supports Android targets') + raise TargetStableError('netstats insturment only supports Android targets') if apk is None: apk = os.path.join(THIS_DIR, 'netstats.apk') if not os.path.isfile(apk): diff --git a/devlib/module/cgroups.py b/devlib/module/cgroups.py index d60860c..5e442fd 100644 --- a/devlib/module/cgroups.py +++ b/devlib/module/cgroups.py @@ -18,7 +18,7 @@ import re from collections import namedtuple from devlib.module import Module -from devlib.exception import TargetError +from devlib.exception import TargetStableError from devlib.utils.misc import list_to_ranges, isiterable from devlib.utils.types import boolean @@ -281,7 +281,7 @@ class CGroup(object): self.target.execute('[ -d {0} ]'\ .format(self.directory), as_root=True) return True - except TargetError: + except TargetStableError: return False def get(self): @@ -319,7 +319,7 @@ class CGroup(object): # Set the attribute value try: self.target.write_value(path, attrs[idx]) - except TargetError: + except TargetStableError: # Check if the error is due to a non-existing attribute attrs = self.get() if idx not in attrs: @@ -389,9 +389,9 @@ class CgroupsModule(Module): controller = Controller(ss.name, hid, hierarchy[hid]) try: controller.mount(self.target, self.cgroup_root) - except TargetError: + except TargetStableError: message = 'Failed to mount "{}" controller' - raise TargetError(message.format(controller.kind)) + raise TargetStableError(message.format(controller.kind)) self.logger.info(' %-12s : %s', controller.kind, controller.mount_point) self.controllers[ss.name] = controller diff --git a/devlib/module/cpufreq.py b/devlib/module/cpufreq.py index 294d083..1f502da 100644 --- a/devlib/module/cpufreq.py +++ b/devlib/module/cpufreq.py @@ -13,7 +13,7 @@ # limitations under the License. # from devlib.module import Module -from devlib.exception import TargetError +from devlib.exception import TargetStableError from devlib.utils.misc import memoized @@ -82,7 +82,7 @@ class CpufreqModule(Module): Setting the governor on any core in a cluster will also set it on all other cores in that cluster. - :raises: TargetError if governor is not supported by the CPU, or if, + :raises: TargetStableError if governor is not supported by the CPU, or if, for some reason, the governor could not be set. """ @@ -90,7 +90,7 @@ class CpufreqModule(Module): cpu = 'cpu{}'.format(cpu) supported = self.list_governors(cpu) if governor not in supported: - raise TargetError('Governor {} not supported for cpu {}'.format(governor, cpu)) + raise TargetStableError('Governor {} not supported for cpu {}'.format(governor, cpu)) sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_governor'.format(cpu) self.target.write_value(sysfile, governor) self.set_governor_tunables(cpu, governor, **kwargs) @@ -104,11 +104,11 @@ class CpufreqModule(Module): try: tunables_path = '/sys/devices/system/cpu/{}/cpufreq/{}'.format(cpu, governor) self._governor_tunables[governor] = self.target.list_directory(tunables_path) - except TargetError: # probably an older kernel + except TargetStableError: # probably an older kernel try: tunables_path = '/sys/devices/system/cpu/cpufreq/{}'.format(governor) self._governor_tunables[governor] = self.target.list_directory(tunables_path) - except TargetError: # governor does not support tunables + except TargetStableError: # governor does not support tunables self._governor_tunables[governor] = [] return self._governor_tunables[governor] @@ -122,7 +122,7 @@ class CpufreqModule(Module): try: path = '/sys/devices/system/cpu/{}/cpufreq/{}/{}'.format(cpu, governor, tunable) tunables[tunable] = self.target.read_value(path) - except TargetError: # May be an older kernel + except TargetStableError: # May be an older kernel path = '/sys/devices/system/cpu/cpufreq/{}/{}'.format(governor, tunable) tunables[tunable] = self.target.read_value(path) return tunables @@ -140,7 +140,7 @@ class CpufreqModule(Module): The rest should be keyword parameters mapping tunable name onto the value to be set for it. - :raises: TargetError if governor specified is not a valid governor name, or if + :raises: TargetStableError if governor specified is not a valid governor name, or if a tunable specified is not valid for the governor, or if could not set tunable. @@ -155,7 +155,7 @@ class CpufreqModule(Module): path = '/sys/devices/system/cpu/{}/cpufreq/{}/{}'.format(cpu, governor, tunable) try: self.target.write_value(path, value) - except TargetError: + except TargetStableError: if self.target.file_exists(path): # File exists but we did something wrong raise @@ -165,7 +165,7 @@ class CpufreqModule(Module): else: message = 'Unexpected tunable {} for governor {} on {}.\n'.format(tunable, governor, cpu) message += 'Available tunables are: {}'.format(valid_tunables) - raise TargetError(message) + raise TargetStableError(message) @memoized def list_frequencies(self, cpu): @@ -177,14 +177,14 @@ class CpufreqModule(Module): cmd = 'cat /sys/devices/system/cpu/{}/cpufreq/scaling_available_frequencies'.format(cpu) output = self.target.execute(cmd) available_frequencies = list(map(int, output.strip().split())) # pylint: disable=E1103 - except TargetError: + except TargetStableError: # On some devices scaling_frequencies is not generated. # http://adrynalyne-teachtofish.blogspot.co.uk/2011/11/how-to-enable-scalingavailablefrequenci.html # Fall back to parsing stats/time_in_state path = '/sys/devices/system/cpu/{}/cpufreq/stats/time_in_state'.format(cpu) try: out_iter = iter(self.target.read_value(path).split()) - except TargetError: + except TargetStableError: if not self.target.file_exists(path): # Probably intel_pstate. Can't get available freqs. return [] @@ -219,7 +219,7 @@ class CpufreqModule(Module): try to read the minimum frequency and the following exception will be raised :: - :raises: TargetError if for some reason the frequency could not be read. + :raises: TargetStableError if for some reason the frequency could not be read. """ if isinstance(cpu, int): @@ -239,7 +239,7 @@ class CpufreqModule(Module): on the device. - :raises: TargetError if the frequency is not supported by the CPU, or if, for + :raises: TargetStableError if the frequency is not supported by the CPU, or if, for some reason, frequency could not be set. :raises: ValueError if ``frequency`` is not an integer. @@ -250,7 +250,7 @@ class CpufreqModule(Module): try: value = int(frequency) if exact and available_frequencies and value not in available_frequencies: - raise TargetError('Can\'t set {} frequency to {}\nmust be in {}'.format(cpu, + raise TargetStableError('Can\'t set {} frequency to {}\nmust be in {}'.format(cpu, value, available_frequencies)) sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_min_freq'.format(cpu) @@ -266,7 +266,7 @@ class CpufreqModule(Module): try to read the current frequency and the following exception will be raised :: - :raises: TargetError if for some reason the frequency could not be read. + :raises: TargetStableError if for some reason the frequency could not be read. """ if isinstance(cpu, int): @@ -288,7 +288,7 @@ class CpufreqModule(Module): on the device (if it exists). - :raises: TargetError if the frequency is not supported by the CPU, or if, for + :raises: TargetStableError if the frequency is not supported by the CPU, or if, for some reason, frequency could not be set. :raises: ValueError if ``frequency`` is not an integer. @@ -300,11 +300,11 @@ class CpufreqModule(Module): if exact: available_frequencies = self.list_frequencies(cpu) if available_frequencies and value not in available_frequencies: - raise TargetError('Can\'t set {} frequency to {}\nmust be in {}'.format(cpu, + raise TargetStableError('Can\'t set {} frequency to {}\nmust be in {}'.format(cpu, value, available_frequencies)) if self.get_governor(cpu) != 'userspace': - raise TargetError('Can\'t set {} frequency; governor must be "userspace"'.format(cpu)) + raise TargetStableError('Can\'t set {} frequency; governor must be "userspace"'.format(cpu)) sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_setspeed'.format(cpu) self.target.write_value(sysfile, value, verify=False) except ValueError: @@ -318,7 +318,7 @@ class CpufreqModule(Module): try to read the maximum frequency and the following exception will be raised :: - :raises: TargetError if for some reason the frequency could not be read. + :raises: TargetStableError if for some reason the frequency could not be read. """ if isinstance(cpu, int): cpu = 'cpu{}'.format(cpu) @@ -337,7 +337,7 @@ class CpufreqModule(Module): on the device. - :raises: TargetError if the frequency is not supported by the CPU, or if, for + :raises: TargetStableError if the frequency is not supported by the CPU, or if, for some reason, frequency could not be set. :raises: ValueError if ``frequency`` is not an integer. @@ -348,7 +348,7 @@ class CpufreqModule(Module): try: value = int(frequency) if exact and available_frequencies and value not in available_frequencies: - raise TargetError('Can\'t set {} frequency to {}\nmust be in {}'.format(cpu, + raise TargetStableError('Can\'t set {} frequency to {}\nmust be in {}'.format(cpu, value, available_frequencies)) sysfile = '/sys/devices/system/cpu/{}/cpufreq/scaling_max_freq'.format(cpu) @@ -409,13 +409,13 @@ class CpufreqModule(Module): return self.target._execute_util( 'cpufreq_set_all_governors {}'.format(governor), as_root=True) - except TargetError as e: + except TargetStableError as e: if ("echo: I/O error" in str(e) or "write error: Invalid argument" in str(e)): cpus_unsupported = [c for c in self.target.list_online_cpus() if governor not in self.list_governors(c)] - raise TargetError("Governor {} unsupported for CPUs {}".format( + raise TargetStableError("Governor {} unsupported for CPUs {}".format( governor, cpus_unsupported)) else: raise diff --git a/devlib/module/devfreq.py b/devlib/module/devfreq.py index 8c5d885..00c3154 100644 --- a/devlib/module/devfreq.py +++ b/devlib/module/devfreq.py @@ -13,7 +13,7 @@ # limitations under the License. # from devlib.module import Module -from devlib.exception import TargetError +from devlib.exception import TargetStableError from devlib.utils.misc import memoized class DevfreqModule(Module): @@ -64,13 +64,13 @@ class DevfreqModule(Module): Additional keyword arguments can be used to specify governor tunables for governors that support them. - :raises: TargetError if governor is not supported by the device, or if, + :raises: TargetStableError if governor is not supported by the device, or if, for some reason, the governor could not be set. """ supported = self.list_governors(device) if governor not in supported: - raise TargetError('Governor {} not supported for device {}'.format(governor, device)) + raise TargetStableError('Governor {} not supported for device {}'.format(governor, device)) sysfile = '/sys/class/devfreq/{}/governor'.format(device) self.target.write_value(sysfile, governor) @@ -94,7 +94,7 @@ class DevfreqModule(Module): will try to read the minimum frequency and the following exception will be raised :: - :raises: TargetError if for some reason the frequency could not be read. + :raises: TargetStableError if for some reason the frequency could not be read. """ sysfile = '/sys/class/devfreq/{}/min_freq'.format(device) @@ -112,7 +112,7 @@ class DevfreqModule(Module): on the device. - :raises: TargetError if the frequency is not supported by the device, or if, for + :raises: TargetStableError if the frequency is not supported by the device, or if, for some reason, frequency could not be set. :raises: ValueError if ``frequency`` is not an integer. @@ -121,7 +121,7 @@ class DevfreqModule(Module): try: value = int(frequency) if exact and available_frequencies and value not in available_frequencies: - raise TargetError('Can\'t set {} frequency to {}\nmust be in {}'.format(device, + raise TargetStableError('Can\'t set {} frequency to {}\nmust be in {}'.format(device, value, available_frequencies)) sysfile = '/sys/class/devfreq/{}/min_freq'.format(device) @@ -137,7 +137,7 @@ class DevfreqModule(Module): will try to read the current frequency and the following exception will be raised :: - :raises: TargetError if for some reason the frequency could not be read. + :raises: TargetStableError if for some reason the frequency could not be read. """ sysfile = '/sys/class/devfreq/{}/cur_freq'.format(device) @@ -151,7 +151,7 @@ class DevfreqModule(Module): try to read the maximum frequency and the following exception will be raised :: - :raises: TargetError if for some reason the frequency could not be read. + :raises: TargetStableError if for some reason the frequency could not be read. """ sysfile = '/sys/class/devfreq/{}/max_freq'.format(device) return self.target.read_int(sysfile) @@ -168,7 +168,7 @@ class DevfreqModule(Module): on the device. - :raises: TargetError if the frequency is not supported by the device, or + :raises: TargetStableError if the frequency is not supported by the device, or if, for some reason, frequency could not be set. :raises: ValueError if ``frequency`` is not an integer. @@ -180,7 +180,7 @@ class DevfreqModule(Module): raise ValueError('Frequency must be an integer; got: "{}"'.format(frequency)) if exact and value not in available_frequencies: - raise TargetError('Can\'t set {} frequency to {}\nmust be in {}'.format(device, + raise TargetStableError('Can\'t set {} frequency to {}\nmust be in {}'.format(device, value, available_frequencies)) sysfile = '/sys/class/devfreq/{}/max_freq'.format(device) @@ -202,13 +202,13 @@ class DevfreqModule(Module): try: return self.target._execute_util( # pylint: disable=protected-access 'devfreq_set_all_governors {}'.format(governor), as_root=True) - except TargetError as e: + except TargetStableError as e: if ("echo: I/O error" in str(e) or "write error: Invalid argument" in str(e)): devs_unsupported = [d for d in self.target.list_devices() if governor not in self.list_governors(d)] - raise TargetError("Governor {} unsupported for devices {}".format( + raise TargetStableError("Governor {} unsupported for devices {}".format( governor, devs_unsupported)) else: raise diff --git a/devlib/module/gem5stats.py b/devlib/module/gem5stats.py index 29b1858..eef848d 100644 --- a/devlib/module/gem5stats.py +++ b/devlib/module/gem5stats.py @@ -17,7 +17,7 @@ import sys import os.path from collections import defaultdict -from devlib.exception import TargetError, HostError +from devlib.exception import TargetStableError, HostError from devlib.module import Module from devlib.platform.gem5 import Gem5SimulationPlatform from devlib.utils.gem5 import iter_statistics_dump, GEM5STATS_ROI_NUMBER @@ -87,13 +87,13 @@ class Gem5StatsModule(Module): if label not in self.rois: raise KeyError('Incorrect ROI label: {}'.format(label)) if not self.rois[label].start(): - raise TargetError('ROI {} was already running'.format(label)) + raise TargetStableError('ROI {} was already running'.format(label)) def roi_end(self, label): if label not in self.rois: raise KeyError('Incorrect ROI label: {}'.format(label)) if not self.rois[label].stop(): - raise TargetError('ROI {} was not running'.format(label)) + raise TargetStableError('ROI {} was not running'.format(label)) def start_periodic_dump(self, delay_ns=0, period_ns=10000000): # Default period is 10ms because it's roughly what's needed to have diff --git a/devlib/module/gpufreq.py b/devlib/module/gpufreq.py index 1cccb27..9f0a952 100644 --- a/devlib/module/gpufreq.py +++ b/devlib/module/gpufreq.py @@ -29,7 +29,7 @@ import re from devlib.module import Module -from devlib.exception import TargetError +from devlib.exception import TargetStableError from devlib.utils.misc import memoized class GpufreqModule(Module): @@ -56,7 +56,7 @@ class GpufreqModule(Module): def set_governor(self, governor): if governor not in self.governors: - raise TargetError('Governor {} not supported for gpu'.format(governor)) + raise TargetStableError('Governor {} not supported for gpu'.format(governor)) self.target.write_value("/sys/kernel/gpu/gpu_governor", governor) def get_frequencies(self): @@ -73,7 +73,7 @@ class GpufreqModule(Module): try to read the current frequency and the following exception will be raised :: - :raises: TargetError if for some reason the frequency could not be read. + :raises: TargetStableError if for some reason the frequency could not be read. """ return int(self.target.read_value("/sys/kernel/gpu/gpu_clock")) diff --git a/devlib/module/hwmon.py b/devlib/module/hwmon.py index 9c037e8..7145ae5 100644 --- a/devlib/module/hwmon.py +++ b/devlib/module/hwmon.py @@ -15,7 +15,7 @@ import re from collections import defaultdict -from devlib import TargetError +from devlib import TargetStableError from devlib.module import Module from devlib.utils.types import integer @@ -118,7 +118,7 @@ class HwmonModule(Module): def probe(target): try: target.list_directory(HWMON_ROOT, as_root=target.is_rooted) - except TargetError: + except TargetStableError: # Doesn't exist or no permissions return False return True diff --git a/devlib/module/vexpress.py b/devlib/module/vexpress.py index 8f22cf7..0f8ab97 100644 --- a/devlib/module/vexpress.py +++ b/devlib/module/vexpress.py @@ -20,7 +20,7 @@ import shutil from subprocess import CalledProcessError from devlib.module import HardRestModule, BootModule, FlashModule -from devlib.exception import TargetError, HostError +from devlib.exception import TargetError, TargetStableError, HostError from devlib.utils.serial_port import open_serial_connection, pulse_dtr, write_characters from devlib.utils.uefi import UefiMenu, UefiConfig from devlib.utils.uboot import UbootMenu @@ -89,7 +89,7 @@ class VexpressReboottxtHardReset(HardRestModule): try: if self.target.is_connected: self.target.execute('sync') - except TargetError: + except (TargetError, CalledProcessError): pass if not os.path.exists(self.path): @@ -225,7 +225,7 @@ class VexpressUefiShellBoot(VexpressBootModule): try: menu.select(self.uefi_entry) except LookupError: - raise TargetError('Did not see "{}" UEFI entry.'.format(self.uefi_entry)) + raise TargetStableError('Did not see "{}" UEFI entry.'.format(self.uefi_entry)) tty.expect(self.efi_shell_prompt, timeout=self.timeout) if self.bootargs: tty.sendline('') # stop default boot @@ -344,7 +344,7 @@ class VersatileExpressFlashModule(FlashModule): os.system('sync') except (IOError, OSError) as e: msg = 'Could not deploy images to {}; got: {}' - raise TargetError(msg.format(self.vemsd_mount, e)) + raise TargetStableError(msg.format(self.vemsd_mount, e)) self.target.boot() self.target.connect(timeout=30) @@ -390,4 +390,4 @@ def wait_for_vemsd(vemsd_mount, tty, mcc_prompt=DEFAULT_MCC_PROMPT, short_delay= time.sleep(short_delay * 3) if os.path.exists(path): return - raise TargetError('Could not mount {}'.format(vemsd_mount)) + raise TargetStableError('Could not mount {}'.format(vemsd_mount)) diff --git a/devlib/platform/arm.py b/devlib/platform/arm.py index ae0f1d9..4dbf98f 100644 --- a/devlib/platform/arm.py +++ b/devlib/platform/arm.py @@ -19,7 +19,7 @@ import tempfile import time import pexpect -from devlib.exception import TargetError, HostError +from devlib.exception import TargetStableError, HostError from devlib.host import PACKAGE_BIN_DIRECTORY from devlib.instrument import (Instrument, InstrumentChannel, MeasurementsCsv, Measurement, CONTINUOUS, INSTANTANEOUS) @@ -123,7 +123,7 @@ class VersatileExpressPlatform(Platform): except pexpect.TIMEOUT: pass # We have our own timeout -- see below. if (time.time() - wait_start_time) > self.ready_timeout: - raise TargetError('Could not acquire IP address.') + raise TargetTransientError('Could not acquire IP address.') finally: tty.sendline('exit') # exit shell created by "su" call at the start diff --git a/devlib/platform/gem5.py b/devlib/platform/gem5.py index 5b067dc..817699a 100644 --- a/devlib/platform/gem5.py +++ b/devlib/platform/gem5.py @@ -19,7 +19,7 @@ import shutil import time import types -from devlib.exception import TargetError +from devlib.exception import TargetStableError from devlib.host import PACKAGE_BIN_DIRECTORY from devlib.platform import Platform from devlib.utils.ssh import AndroidGem5Connection, LinuxGem5Connection @@ -86,12 +86,12 @@ class Gem5SimulationPlatform(Platform): Check if the command to start gem5 makes sense """ if self.gem5args_binary is None: - raise TargetError('Please specify a gem5 binary.') + raise TargetStableError('Please specify a gem5 binary.') if self.gem5args_args is None: - raise TargetError('Please specify the arguments passed on to gem5.') + raise TargetStableError('Please specify the arguments passed on to gem5.') self.gem5args_virtio = str(self.gem5args_virtio).format(self.gem5_interact_dir) if self.gem5args_virtio is None: - raise TargetError('Please specify arguments needed for virtIO.') + raise TargetStableError('Please specify arguments needed for virtIO.') def _start_interaction_gem5(self): """ @@ -110,7 +110,7 @@ class Gem5SimulationPlatform(Platform): if not os.path.exists(self.stats_directory): os.mkdir(self.stats_directory) if os.path.exists(self.gem5_out_dir): - raise TargetError("The gem5 stats directory {} already " + raise TargetStableError("The gem5 stats directory {} already " "exists.".format(self.gem5_out_dir)) else: os.mkdir(self.gem5_out_dir) @@ -153,7 +153,7 @@ class Gem5SimulationPlatform(Platform): e.g. pid, input directory etc """ self.logger("This functionality is not yet implemented") - raise TargetError() + raise TargetStableError() def _intercept_telnet_port(self): """ @@ -161,13 +161,13 @@ class Gem5SimulationPlatform(Platform): """ if self.gem5 is None: - raise TargetError('The platform has no gem5 simulation! ' + raise TargetStableError('The platform has no gem5 simulation! ' 'Something went wrong') while self.gem5_port is None: # Check that gem5 is running! if self.gem5.poll(): message = "The gem5 process has crashed with error code {}!\n\tPlease see {} for details." - raise TargetError(message.format(self.gem5.poll(), self.stderr_file.name)) + raise TargetStableError(message.format(self.gem5.poll(), self.stderr_file.name)) # Open the stderr file with open(self.stderr_filename, 'r') as f: @@ -185,7 +185,7 @@ class Gem5SimulationPlatform(Platform): # Check if the sockets are not disabled m = re.search(r"Sockets disabled, not accepting terminal connections", line) if m: - raise TargetError("The sockets have been disabled!" + raise TargetStableError("The sockets have been disabled!" "Pass --listener-mode=on to gem5") else: time.sleep(1) @@ -287,10 +287,10 @@ class Gem5SimulationPlatform(Platform): # Methods that will be monkey-patched onto the target def _overwritten_reset(self): # pylint: disable=unused-argument - raise TargetError('Resetting is not allowed on gem5 platforms!') + raise TargetStableError('Resetting is not allowed on gem5 platforms!') def _overwritten_reboot(self): # pylint: disable=unused-argument - raise TargetError('Rebooting is not allowed on gem5 platforms!') + raise TargetStableError('Rebooting is not allowed on gem5 platforms!') def _overwritten_capture_screen(self, filepath): connection_screencapped = self.platform.gem5_capture_screen(filepath) diff --git a/devlib/target.py b/devlib/target.py index 44275d1..5c4cfd2 100644 --- a/devlib/target.py +++ b/devlib/target.py @@ -29,7 +29,7 @@ from collections import namedtuple from devlib.host import LocalConnection, PACKAGE_BIN_DIRECTORY from devlib.module import get_module from devlib.platform import Platform -from devlib.exception import TargetError, TargetNotRespondingError, TimeoutError # pylint: disable=redefined-builtin +from devlib.exception import DevlibTransientError, TargetStableError, TargetNotRespondingError, TimeoutError # pylint: disable=redefined-builtin from devlib.utils.ssh import SshConnection from devlib.utils.android import AdbConnection, AndroidProperties, LogcatMonitor, adb_command, adb_disconnect, INTENT_FLAGS from devlib.utils.misc import memoized, isiterable, convert_new_lines @@ -104,7 +104,7 @@ class Target(object): try: self.execute('ls /', timeout=5, as_root=True) return True - except (TargetError, TimeoutError): + except (TargetStableError, TimeoutError): return False @property @@ -150,11 +150,11 @@ class Target(object): def config(self): try: return KernelConfig(self.execute('zcat /proc/config.gz')) - except TargetError: + except TargetStableError: for path in ['/boot/config', '/boot/config-$(uname -r)']: try: return KernelConfig(self.execute('cat {}'.format(path))) - except TargetError: + except TargetStableError: pass return KernelConfig('') @@ -203,7 +203,7 @@ class Target(object): # Check if the user hasn't given two different platforms if 'platform' in self.connection_settings: if connection_settings['platform'] is not platform: - raise TargetError('Platform specified in connection_settings ' + raise TargetStableError('Platform specified in connection_settings ' '({}) differs from that directly passed ' '({})!)' .format(connection_settings['platform'], @@ -282,14 +282,14 @@ class Target(object): def reboot(self, hard=False, connect=True, timeout=180): if hard: if not self.has('hard_reset'): - raise TargetError('Hard reset not supported for this target.') + raise TargetStableError('Hard reset not supported for this target.') self.hard_reset() # pylint: disable=no-member else: if not self.is_connected: message = 'Cannot reboot target becuase it is disconnected. ' +\ 'Either connect() first, or specify hard=True ' +\ '(in which case, a hard_reset module must be installed)' - raise TargetError(message) + raise TargetTransientError(message) self.reset() # Wait a fixed delay before starting polling to give the target time to # shut down, otherwise, might create the connection while it's still shutting @@ -346,7 +346,7 @@ class Target(object): try: self.execute('{} tar -cvf {} {}'.format(self.busybox, tar_file_name, source_dir), as_root=as_root) - except TargetError: + except TargetStableError: self.logger.debug('Failed to run tar command on target! ' \ 'Not pulling directory {}'.format(source_dir)) # Pull the file @@ -360,8 +360,10 @@ class Target(object): # execution - def execute(self, command, timeout=None, check_exit_code=True, as_root=False): - return self.conn.execute(command, timeout, check_exit_code, as_root) + def execute(self, command, timeout=None, check_exit_code=True, + as_root=False, will_succeed=False): + return self.conn.execute(command, timeout, check_exit_code, as_root, + will_succeed) def background(self, command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, as_root=False): return self.conn.background(command, stdout, stderr, as_root) @@ -459,12 +461,12 @@ class Target(object): output = self.read_value(path) if not output == value: message = 'Could not set the value of {} to "{}" (read "{}")'.format(path, value, output) - raise TargetError(message) + raise TargetStableError(message) def reset(self): try: self.execute('reboot', as_root=self.needs_su, timeout=2) - except (TargetError, TimeoutError, subprocess.CalledProcessError): + except (DevlibTransientError, subprocess.CalledProcessError): # on some targets "reboot" doesn't return gracefully pass self._connected_as_root = None @@ -473,7 +475,7 @@ class Target(object): try: self.conn.execute('ls /', timeout=5) return 1 - except (TimeoutError, subprocess.CalledProcessError, TargetError): + except (DevlibTransientError, subprocess.CalledProcessError): if explode: raise TargetNotRespondingError('Target {} is not responding'.format(self.conn.name)) return 0 @@ -488,7 +490,7 @@ class Target(object): for pid in self.get_pids_of(process_name): try: self.kill(pid, signal=signal, as_root=as_root) - except TargetError: + except TargetStableError: pass def get_pids_of(self, process_name): @@ -588,7 +590,7 @@ class Target(object): try: if name in self.list_directory(path): return self.path.join(path, name) - except TargetError: + except TargetStableError: pass # directory does not exist or no executable permissions which = get_installed @@ -779,7 +781,7 @@ class Target(object): try: self.execute(command) return True - except TargetError as e: + except TargetStableError as e: err = str(e).lower() if '100% packet loss' in err: # We sent a packet but got no response. @@ -823,15 +825,12 @@ class LinuxTarget(Target): @memoized def os_version(self): os_version = {} - try: - command = 'ls /etc/*-release /etc*-version /etc/*_release /etc/*_version 2>/dev/null' - version_files = self.execute(command, check_exit_code=False).strip().split() - for vf in version_files: - name = self.path.basename(vf) - output = self.read_value(vf) - os_version[name] = convert_new_lines(output.strip()).replace('\n', ' ') - except TargetError: - raise + command = 'ls /etc/*-release /etc*-version /etc/*_release /etc/*_version 2>/dev/null' + version_files = self.execute(command, check_exit_code=False).strip().split() + for vf in version_files: + name = self.path.basename(vf) + output = self.read_value(vf) + os_version[name] = convert_new_lines(output.strip()).replace('\n', ' ') return os_version @property @@ -938,7 +937,7 @@ class LinuxTarget(Target): filepath = filepath.format(ts=ts) self.pull(tmpfile, filepath) self.remove(tmpfile) - except TargetError as e: + except TargetStableError as e: if "Can't open X dispay." not in e.message: raise e message = e.message.split('OUTPUT:', 1)[1].strip() # pylint: disable=no-member @@ -1079,7 +1078,7 @@ class AndroidTarget(Target): try: self.execute('reboot {}'.format(fastboot and 'fastboot' or ''), as_root=self.needs_su, timeout=2) - except (TargetError, TimeoutError, subprocess.CalledProcessError): + except (DevlibTransientError, subprocess.CalledProcessError): # on some targets "reboot" doesn't return gracefully pass self._connected_as_root = None @@ -1091,7 +1090,9 @@ class AndroidTarget(Target): time.sleep(5) boot_completed = boolean(self.getprop('sys.boot_completed')) if not boot_completed: - raise TargetError('Connected but Android did not fully boot.') + # Raise a TargetStableError as this usually happens because of + # an issue with Android more than a timeout that is too small. + raise TargetStableError('Connected but Android did not fully boot.') def connect(self, timeout=30, check_boot_completed=True): # pylint: disable=arguments-differ device = self.connection_settings.get('device') @@ -1128,7 +1129,7 @@ class AndroidTarget(Target): self.ls_command = 'ls -1' try: self.execute('ls -1 {}'.format(self.working_directory), as_root=False) - except TargetError: + except TargetStableError: self.ls_command = 'ls' def list_directory(self, path, as_root=False): @@ -1240,7 +1241,7 @@ class AndroidTarget(Target): swipe_height = height * 2 // 3 self.input_swipe(swipe_middle, swipe_height, swipe_middle, 0) else: - raise TargetError("Invalid swipe direction: {}".format(direction)) + raise TargetStableError("Invalid swipe direction: {}".format(direction)) def getprop(self, prop=None): props = AndroidProperties(self.execute('getprop')) @@ -1307,12 +1308,12 @@ class AndroidTarget(Target): self.logger.debug("Replace APK = {}, ADB flags = '{}'".format(replace, ' '.join(flags))) return adb_command(self.adb_name, "install {} '{}'".format(' '.join(flags), filepath), timeout=timeout) else: - raise TargetError('Can\'t install {}: unsupported format.'.format(filepath)) + raise TargetStableError('Can\'t install {}: unsupported format.'.format(filepath)) def grant_package_permission(self, package, permission): try: return self.execute('pm grant {} {}'.format(package, permission)) - except TargetError as e: + except TargetStableError as e: if 'is not a changeable permission type' in e.message: pass # Ignore if unchangeable elif 'Unknown permission' in e.message: @@ -1411,7 +1412,7 @@ class AndroidTarget(Target): if match: return boolean(match.group(1)) else: - raise TargetError('Could not establish screen state.') + raise TargetStableError('Could not establish screen state.') def ensure_screen_is_on(self): if not self.is_screen_on(): @@ -1456,7 +1457,7 @@ class AndroidTarget(Target): def set_airplane_mode(self, mode): root_required = self.get_sdk_version() > 23 if root_required and not self.is_rooted: - raise TargetError('Root is required to toggle airplane mode on Android 7+') + raise TargetStableError('Root is required to toggle airplane mode on Android 7+') mode = int(boolean(mode)) cmd = 'settings put global airplane_mode_on {}' self.execute(cmd.format(mode)) @@ -1540,7 +1541,7 @@ class AndroidTarget(Target): as_root=True) else: message = 'Could not find mount point for executables directory {}' - raise TargetError(message.format(self.executables_directory)) + raise TargetStableError(message.format(self.executables_directory)) _charging_enabled_path = '/sys/class/power_supply/battery/charging_enabled' diff --git a/devlib/trace/ftrace.py b/devlib/trace/ftrace.py index 9f08985..8fef36c 100644 --- a/devlib/trace/ftrace.py +++ b/devlib/trace/ftrace.py @@ -23,7 +23,7 @@ import sys from devlib.trace import TraceCollector from devlib.host import PACKAGE_BIN_DIRECTORY -from devlib.exception import TargetError, HostError +from devlib.exception import TargetStableError, HostError from devlib.utils.misc import check_output, which @@ -99,7 +99,7 @@ class FtraceCollector(TraceCollector): self.kernelshark = which('kernelshark') if not self.target.is_rooted: - raise TargetError('trace-cmd instrument cannot be used on an unrooted device.') + raise TargetStableError('trace-cmd instrument cannot be used on an unrooted device.') if self.autoreport and not self.report_on_target and self.host_binary is None: raise HostError('trace-cmd binary must be installed on the host if autoreport=True.') if self.autoview and self.kernelshark is None: @@ -109,7 +109,7 @@ class FtraceCollector(TraceCollector): self.target_binary = self.target.install(host_file) else: if not self.target.is_installed('trace-cmd'): - raise TargetError('No trace-cmd found on device and no_install=True is specified.') + raise TargetStableError('No trace-cmd found on device and no_install=True is specified.') self.target_binary = 'trace-cmd' # Validate required events to be traced @@ -127,7 +127,7 @@ class FtraceCollector(TraceCollector): if not list(filter(event_re.match, available_events)): message = 'Event [{}] not available for tracing'.format(event) if strict: - raise TargetError(message) + raise TargetStableError(message) self.target.logger.warning(message) else: selected_events.append(event) @@ -142,7 +142,7 @@ class FtraceCollector(TraceCollector): # Check for function tracing support if self.functions: if not self.target.file_exists(self.function_profile_file): - raise TargetError('Function profiling not supported. '\ + raise TargetStableError('Function profiling not supported. '\ 'A kernel build with CONFIG_FUNCTION_PROFILER enable is required') # Validate required functions to be traced available_functions = self.target.execute( @@ -153,7 +153,7 @@ class FtraceCollector(TraceCollector): if function not in available_functions: message = 'Function [{}] not available for profiling'.format(function) if strict: - raise TargetError(message) + raise TargetStableError(message) self.target.logger.warning(message) else: selected_functions.append(function) @@ -283,7 +283,7 @@ class FtraceCollector(TraceCollector): if sys.version_info[0] == 3: error = error.decode(sys.stdout.encoding, 'replace') if process.returncode: - raise TargetError('trace-cmd returned non-zero exit code {}'.format(process.returncode)) + raise TargetStableError('trace-cmd returned non-zero exit code {}'.format(process.returncode)) if error: # logged at debug level, as trace-cmd always outputs some # errors that seem benign. diff --git a/devlib/trace/systrace.py b/devlib/trace/systrace.py index c150a96..2f889c0 100644 --- a/devlib/trace/systrace.py +++ b/devlib/trace/systrace.py @@ -34,7 +34,7 @@ import subprocess from shutil import copyfile from tempfile import NamedTemporaryFile -from devlib.exception import TargetError, HostError +from devlib.exception import TargetStableError, HostError from devlib.trace import TraceCollector from devlib.utils.android import platform_tools from devlib.utils.misc import memoized @@ -109,12 +109,12 @@ class SystraceCollector(TraceCollector): if category not in self.available_categories: message = 'Category [{}] not available for tracing'.format(category) if strict: - raise TargetError(message) + raise TargetStableError(message) self.logger.warning(message) self.categories = list(set(self.categories) & set(self.available_categories)) if not self.categories: - raise TargetError('None of the requested categories are available') + raise TargetStableError('None of the requested categories are available') def __del__(self): self.reset() diff --git a/devlib/utils/android.py b/devlib/utils/android.py index 029c6d4..604d820 100755 --- a/devlib/utils/android.py +++ b/devlib/utils/android.py @@ -29,7 +29,7 @@ import subprocess from collections import defaultdict import pexpect -from devlib.exception import TargetError, HostError +from devlib.exception import TargetTransientError, TargetStableError, HostError, DevlibError from devlib.utils.misc import check_output, which, ABI_MAP from devlib.utils.misc import escape_single_quotes, escape_double_quotes @@ -257,9 +257,15 @@ class AdbConnection(object): # pylint: disable=unused-argument def execute(self, command, timeout=None, check_exit_code=False, - as_root=False, strip_colors=True): - return adb_shell(self.device, command, timeout, check_exit_code, - as_root, adb_server=self.adb_server) + as_root=False, strip_colors=True, will_succeed=False): + try: + return adb_shell(self.device, command, timeout, check_exit_code, + as_root, adb_server=self.adb_server) + except TargetStableError as e: + if will_succeed: + raise TargetTransientError(e) + else: + raise def background(self, command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, as_root=False): return adb_background_shell(self.device, command, stdout, stderr, as_root) @@ -357,7 +363,7 @@ def adb_disconnect(device): logger.debug(command) retval = subprocess.call(command, stdout=open(os.devnull, 'wb'), shell=True) if retval: - raise TargetError('"{}" returned {}'.format(command, retval)) + raise TargetTransientError('"{}" returned {}'.format(command, retval)) def _ping(device): @@ -394,7 +400,7 @@ def adb_shell(device, command, timeout=None, check_exit_code=False, try: raw_output, _ = check_output(actual_command, timeout, shell=False, combined_output=True) except subprocess.CalledProcessError as e: - raise TargetError(str(e)) + raise TargetStableError(str(e)) if raw_output: try: @@ -413,19 +419,19 @@ def adb_shell(device, command, timeout=None, check_exit_code=False, if int(exit_code): message = ('Got exit code {}\nfrom target command: {}\n' 'OUTPUT: {}') - raise TargetError(message.format(exit_code, command, output)) + raise TargetStableError(message.format(exit_code, command, output)) elif re_search: message = 'Could not start activity; got the following:\n{}' - raise TargetError(message.format(re_search[0])) + raise TargetStableError(message.format(re_search[0])) else: # not all digits if re_search: message = 'Could not start activity; got the following:\n{}' - raise TargetError(message.format(re_search[0])) + raise TargetStableError(message.format(re_search[0])) else: message = 'adb has returned early; did not get an exit code. '\ 'Was kill-server invoked?\nOUTPUT:\n-----\n{}\n'\ '-----' - raise TargetError(message.format(raw_output)) + raise TargetTransientError(message.format(raw_output)) return output @@ -484,7 +490,7 @@ def grant_app_permissions(target, package): for permission in permissions: try: target.execute('pm grant {} {}'.format(package, permission)) - except TargetError: + except TargetStableError: logger.debug('Cannot grant {}'.format(permission)) diff --git a/devlib/utils/ssh.py b/devlib/utils/ssh.py index 1342cf2..6b87025 100644 --- a/devlib/utils/ssh.py +++ b/devlib/utils/ssh.py @@ -36,7 +36,8 @@ else: from pexpect import EOF, TIMEOUT, spawn # pylint: disable=redefined-builtin,wrong-import-position -from devlib.exception import HostError, TargetError, TimeoutError +from devlib.exception import (HostError, TargetStableError, TargetNotRespondingError, + TimeoutError) from devlib.utils.misc import which, strip_bash_colors, check_output from devlib.utils.misc import (escape_single_quotes, escape_double_quotes, escape_spaces) @@ -72,7 +73,7 @@ def ssh_get_shell(host, username, password=None, keyfile=None, port=None, timeou timeout -= time.time() - start_time if timeout <= 0: message = 'Could not connect to {}; is the host name correct?' - raise TargetError(message.format(host)) + raise TargetTransientError(message.format(host)) time.sleep(5) conn.setwinsize(500, 200) @@ -194,7 +195,7 @@ class SshConnection(object): return self._scp(source, dest, timeout) def execute(self, command, timeout=None, check_exit_code=True, - as_root=False, strip_colors=True): #pylint: disable=unused-argument + as_root=False, strip_colors=True, will_succeed=False): #pylint: disable=unused-argument if command == '': # Empty command is valid but the __devlib_ec stuff below will # produce a syntax error with bash. Treat as a special case. @@ -210,14 +211,19 @@ class SshConnection(object): exit_code = int(exit_code_text) if exit_code: message = 'Got exit code {}\nfrom: {}\nOUTPUT: {}' - raise TargetError(message.format(exit_code, command, output)) + raise TargetStableError(message.format(exit_code, command, output)) except (ValueError, IndexError): logger.warning( 'Could not get exit code for "{}",\ngot: "{}"'\ .format(command, exit_code_text)) return output except EOF: - raise TargetError('Connection lost.') + raise TargetNotRespondingError('Connection lost.') + except TargetStableError as e: + if will_succeed: + raise TargetTransientError(e) + else: + raise def background(self, command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, as_root=False): try: @@ -231,7 +237,7 @@ class SshConnection(object): command = _give_password(self.password, command) return subprocess.Popen(command, stdout=stdout, stderr=stderr, shell=True) except EOF: - raise TargetError('Connection lost.') + raise TargetNotRespondingError('Connection lost.') def close(self): logger.debug('Logging out {}@{}'.format(self.username, self.host)) @@ -352,7 +358,7 @@ class Gem5Connection(TelnetConnection): if host is not None: host_system = socket.gethostname() if host_system != host: - raise TargetError("Gem5Connection can only connect to gem5 " + raise TargetStableError("Gem5Connection can only connect to gem5 " "simulations on your current host {}, which " "differs from the one given {}!" .format(host_system, host)) @@ -497,16 +503,23 @@ class Gem5Connection(TelnetConnection): logger.debug("Pull complete.") def execute(self, command, timeout=1000, check_exit_code=True, - as_root=False, strip_colors=True): + as_root=False, strip_colors=True, will_succeed=False): """ Execute a command on the gem5 platform """ # First check if the connection is set up to interact with gem5 self._check_ready() - output = self._gem5_shell(command, - check_exit_code=check_exit_code, - as_root=as_root) + try: + output = self._gem5_shell(command, + check_exit_code=check_exit_code, + as_root=as_root) + except TargetStableError as e: + if will_succeed: + raise TargetTransientError(e) + else: + raise + if strip_colors: output = strip_bash_colors(output) return output @@ -576,7 +589,7 @@ class Gem5Connection(TelnetConnection): # rather than spewing out pexpect errors. if gem5_simulation.poll(): message = "The gem5 process has crashed with error code {}!\n\tPlease see {} for details." - raise TargetError(message.format(gem5_simulation.poll(), gem5_out_dir)) + raise TargetNotRespondingError(message.format(gem5_simulation.poll(), gem5_out_dir)) else: # Let's re-throw the exception in this case. raise err @@ -604,7 +617,7 @@ class Gem5Connection(TelnetConnection): lock_file_name = '{}{}_{}.LOCK'.format(self.lock_directory, host, port) if os.path.isfile(lock_file_name): # There is already a connection to this gem5 simulation - raise TargetError('There is already a connection to the gem5 ' + raise TargetStableError('There is already a connection to the gem5 ' 'simulation using port {} on {}!' .format(port, host)) @@ -623,7 +636,7 @@ class Gem5Connection(TelnetConnection): self._gem5_EOF_handler(gem5_simulation, gem5_out_dir, err) else: gem5_simulation.kill() - raise TargetError("Failed to connect to the gem5 telnet session.") + raise TargetNotRespondingError("Failed to connect to the gem5 telnet session.") gem5_logger.info("Connected! Waiting for prompt...") @@ -718,7 +731,7 @@ class Gem5Connection(TelnetConnection): def _gem5_util(self, command): """ Execute a gem5 utility command using the m5 binary on the device """ if self.m5_path is None: - raise TargetError('Path to m5 binary on simulated system is not set!') + raise TargetStableError('Path to m5 binary on simulated system is not set!') self._gem5_shell('{} {}'.format(self.m5_path, command)) def _gem5_shell(self, command, as_root=False, timeout=None, check_exit_code=True, sync=True): # pylint: disable=R0912 @@ -732,7 +745,7 @@ class Gem5Connection(TelnetConnection): fails, warn, but continue with the potentially wrong output. The exit code is also checked by default, and non-zero exit codes will - raise a TargetError. + raise a TargetStableError. """ if sync: self._sync_gem5_shell() @@ -788,7 +801,7 @@ class Gem5Connection(TelnetConnection): exit_code = int(exit_code_text.split()[0]) if exit_code: message = 'Got exit code {}\nfrom: {}\nOUTPUT: {}' - raise TargetError(message.format(exit_code, command, output)) + raise TargetStableError(message.format(exit_code, command, output)) except (ValueError, IndexError): gem5_logger.warning('Could not get exit code for "{}",\ngot: "{}"'.format(command, exit_code_text)) @@ -843,7 +856,7 @@ class Gem5Connection(TelnetConnection): Check if the gem5 platform is ready """ if not self.ready: - raise TargetError('Gem5 is not ready to interact yet') + raise TargetTransientError('Gem5 is not ready to interact yet') def _wait_for_boot(self): pass