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:
parent
d6d322c8ac
commit
511d478164
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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...')
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"))
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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()
|
||||
|
@ -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))
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user