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