1
0
mirror of https://github.com/ARM-software/devlib.git synced 2025-01-31 02:00:45 +00:00

exceptions: Classify transient exceptions

Exceptions such as TargetError can sometimes be raised because of a
network issue, which is useful to distinguish from errors caused by a
missing feature for automated testing environments.

The following exceptions are introduced:
* DevlibStableError: raised when a non-transient error is encountered
    * TargetStableError

* DevlibTransientError: raised when a transient error is encountered,
including timeouts.
    * TargetTransientError

When there is an ambiguity on the type of exception to use, it can be
assumed that the configuration is correct, and therefore it is a
transient error, unless the function is specifically designed to probe a
property of the system. In that case, ambiguity is allowed to be lifted
by assuming a non-transient error, since we expect it to raise an
exception when that property is not met. Such ambiguous case can appear
when checking Android has booted, since we cannot know if this is a
timeout/connection issue, or an actual issue with the Android build or
configuration. Another case are the execute() methods, which can be
expected to fail on purpose. A new parameter will_succeed=False is
added, to automatically turn non transient errors into transient ones if
the caller is 100% sure that the command cannot fail unless there is an
environment issue that is outside of the scope controlled by the user.

devlib now never raises TargetError directly, but one of
TargetStableError or TargetTransientError. External code can therefore
rely on all (indirect) instances TargetError to be in either category.
Most existing uses of TargetError are replaced by TargetStableError.
This commit is contained in:
Douglas RAILLARD 2018-06-20 15:04:12 +01:00 committed by Marc Bonnici
parent d6d322c8ac
commit 511d478164
20 changed files with 209 additions and 155 deletions

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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...')

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"))

View File

@ -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

View File

@ -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))

View File

@ -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

View File

@ -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)

View File

@ -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'

View File

@ -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.

View File

@ -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()

View File

@ -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))

View File

@ -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