Skip to content

Commit

Permalink
Merge pull request #2169 from douglas-raillard-arm/_home_pr90386
Browse files Browse the repository at this point in the history
update subtrees
  • Loading branch information
douglas-raillard-arm authored Jan 15, 2024
2 parents 2023b39 + c4d328c commit a94c533
Show file tree
Hide file tree
Showing 33 changed files with 575 additions and 416 deletions.
9 changes: 7 additions & 2 deletions external/devlib/devlib/collector/dmesg.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,13 @@ def __init__(self, target, level=LOG_LEVELS[-1], facility='kern', empty_buffer=F
self.basic_dmesg = '--force-prefix' not in \
self.target.execute('dmesg -h', check_exit_code=False)
self.facility = facility
self.needs_root = bool(target.config.typed_config.get(
'CONFIG_SECURITY_DMESG_RESTRICT', KernelConfigTristate.NO))
try:
needs_root = target.read_sysctl('kernel.dmesg_restrict')
except ValueError:
needs_root = True
else:
needs_root = bool(int(needs_root))
self.needs_root = needs_root

self._begin_timestamp = None
self.empty_buffer = empty_buffer
Expand Down
3 changes: 1 addition & 2 deletions external/devlib/devlib/collector/ftrace.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,8 +408,7 @@ def report(self, binfile, destfile):
self.logger.debug(command)
process = subprocess.Popen(command, stderr=subprocess.PIPE, shell=True)
_, error = process.communicate()
if sys.version_info[0] == 3:
error = error.decode(sys.stdout.encoding or 'utf-8', 'replace')
error = error.decode(sys.stdout.encoding or 'utf-8', 'replace')
if process.returncode:
raise TargetStableError('trace-cmd returned non-zero exit code {}'.format(process.returncode))
if error:
Expand Down
5 changes: 1 addition & 4 deletions external/devlib/devlib/instrument/acmecape.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,7 @@ def stop(self):
msg = 'Could not terminate iio-capture:\n{}'
raise HostError(msg.format(output))
if self.process.returncode != 15: # iio-capture exits with 15 when killed
if sys.version_info[0] == 3:
output += self.process.stdout.read().decode(sys.stdout.encoding or 'utf-8', 'replace')
else:
output += self.process.stdout.read()
output += self.process.stdout.read().decode(sys.stdout.encoding or 'utf-8', 'replace')
self.logger.info('ACME instrument encountered an error, '
'you may want to try rebooting the ACME device:\n'
' ssh root@{} reboot'.format(self.host))
Expand Down
5 changes: 2 additions & 3 deletions external/devlib/devlib/instrument/energy_probe.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,8 @@ def stop(self):
self.process.poll()
if self.process.returncode is not None:
stdout, stderr = self.process.communicate()
if sys.version_info[0] == 3:
stdout = stdout.decode(sys.stdout.encoding or 'utf-8', 'replace')
stderr = stderr.decode(sys.stdout.encoding or 'utf-8', 'replace')
stdout = stdout.decode(sys.stdout.encoding or 'utf-8', 'replace')
stderr = stderr.decode(sys.stdout.encoding or 'utf-8', 'replace')
raise HostError(
'Energy Probe: Caiman exited unexpectedly with exit code {}.\n'
'stdout:\n{}\nstderr:\n{}'.format(self.process.returncode,
Expand Down
5 changes: 2 additions & 3 deletions external/devlib/devlib/instrument/monsoon.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,8 @@ def stop(self):
process.poll()
if process.returncode is not None:
stdout, stderr = process.communicate()
if sys.version_info[0] == 3:
stdout = stdout.encode(sys.stdout.encoding or 'utf-8')
stderr = stderr.encode(sys.stdout.encoding or 'utf-8')
stdout = stdout.encode(sys.stdout.encoding or 'utf-8')
stderr = stderr.encode(sys.stdout.encoding or 'utf-8')
raise HostError(
'Monsoon script exited unexpectedly with exit code {}.\n'
'stdout:\n{}\nstderr:\n{}'.format(process.returncode,
Expand Down
124 changes: 107 additions & 17 deletions external/devlib/devlib/module/thermal.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@
# limitations under the License.

import re
import logging
import devlib.utils.asyn as asyn

from devlib.module import Module
from devlib.exception import TargetStableCalledProcessError

class TripPoint(object):
def __init__(self, zone, _id):
Expand All @@ -27,48 +30,103 @@ def __init__(self, zone, _id):
def target(self):
return self.zone.target

def get_temperature(self):
@asyn.asyncf
async def get_temperature(self):
"""Returns the currently configured temperature of the trip point"""
temp_file = self.target.path.join(self.zone.path, self.temp_node)
return self.target.read_int(temp_file)
return await self.target.read_int.asyn(temp_file)

def set_temperature(self, temperature):
@asyn.asyncf
async def set_temperature(self, temperature):
temp_file = self.target.path.join(self.zone.path, self.temp_node)
self.target.write_value(temp_file, temperature)
await self.target.write_value.asyn(temp_file, temperature)

def get_type(self):
@asyn.asyncf
async def get_type(self):
"""Returns the type of trip point"""
type_file = self.target.path.join(self.zone.path, self.type_node)
return self.target.read_value(type_file)
return await self.target.read_value.asyn(type_file)

class ThermalZone(object):
def __init__(self, target, root, _id):
self.target = target
self.name = 'thermal_zone' + _id
self.path = target.path.join(root, self.name)
self.trip_points = {}
self.type = self.target.read_value(self.target.path.join(self.path, 'type'))

for entry in self.target.list_directory(self.path, as_root=target.is_rooted):
re_match = re.match('^trip_point_([0-9]+)_temp', entry)
if re_match is not None:
self.add_trip_point(re_match.group(1))
self._add_trip_point(re_match.group(1))

def add_trip_point(self, _id):
def _add_trip_point(self, _id):
self.trip_points[int(_id)] = TripPoint(self, _id)

def is_enabled(self):
@asyn.asyncf
async def is_enabled(self):
"""Returns a boolean representing the 'mode' of the thermal zone"""
value = self.target.read_value(self.target.path.join(self.path, 'mode'))
value = await self.target.read_value.asyn(self.target.path.join(self.path, 'mode'))
return value == 'enabled'

def set_enabled(self, enabled=True):
@asyn.asyncf
async def set_enabled(self, enabled=True):
value = 'enabled' if enabled else 'disabled'
self.target.write_value(self.target.path.join(self.path, 'mode'), value)
await self.target.write_value.asyn(self.target.path.join(self.path, 'mode'), value)

def get_temperature(self):
@asyn.asyncf
async def get_temperature(self):
"""Returns the temperature of the thermal zone"""
temp_file = self.target.path.join(self.path, 'temp')
return self.target.read_int(temp_file)
sysfs_temperature_file = self.target.path.join(self.path, 'temp')
return await self.target.read_int.asyn(sysfs_temperature_file)

@asyn.asyncf
async def get_policy(self):
"""Returns the policy of the thermal zone"""
temp_file = self.target.path.join(self.path, 'policy')
return await self.target.read_value.asyn(temp_file)

@asyn.asyncf
async def set_policy(self, policy):
"""
Sets the policy of the thermal zone
:params policy: Thermal governor name
:type policy: str
"""
await self.target.write_value.asyn(self.target.path.join(self.path, 'policy'), policy)

@asyn.asyncf
async def get_offset(self):
"""Returns the temperature offset of the thermal zone"""
offset_file = self.target.path.join(self.path, 'offset')
return await self.target.read_value.asyn(offset_file)

@asyn.asyncf
async def set_offset(self, offset):
"""
Sets the temperature offset in milli-degrees of the thermal zone
:params offset: Temperature offset in milli-degrees
:type policy: int
"""
await self.target.write_value.asyn(self.target.path.join(self.path, 'offset'), policy)

@asyn.asyncf
async def set_emul_temp(self, offset):
"""
Sets the emulated temperature in milli-degrees of the thermal zone
:params offset: Emulated temperature in milli-degrees
:type policy: int
"""
await self.target.write_value.asyn(self.target.path.join(self.path, 'emul_temp'), policy)

@asyn.asyncf
async def get_available_policies(self):
"""Returns the policies available for the thermal zone"""
temp_file = self.target.path.join(self.path, 'available_policies')
return await self.target.read_value.asyn(temp_file)

class ThermalModule(Module):
name = 'thermal'
Expand All @@ -83,6 +141,9 @@ def probe(target):
def __init__(self, target):
super(ThermalModule, self).__init__(target)

self.logger = logging.getLogger(self.name)
self.logger.debug('Initialized [%s] module', self.name)

self.zones = {}
self.cdevs = []

Expand All @@ -93,15 +154,44 @@ def __init__(self, target):
continue

if re_match.group(1) == 'thermal_zone':
self.add_thermal_zone(re_match.group(2))
self._add_thermal_zone(re_match.group(2))
elif re_match.group(1) == 'cooling_device':
# TODO
pass

def add_thermal_zone(self, _id):
def _add_thermal_zone(self, _id):
self.zones[int(_id)] = ThermalZone(self.target, self.thermal_root, _id)

def disable_all_zones(self):
"""Disables all the thermal zones in the target"""
for zone in self.zones.values():
zone.set_enabled(False)

@asyn.asyncf
async def get_all_temperatures(self, error='raise'):
"""
Returns dictionary with current reading of all thermal zones.
:params error: Sensor read error handling (raise or ignore)
:type error: str
:returns: a dictionary in the form: {tz_type:temperature}
"""

async def get_temperature_noexcep(item):
tzid, tz = item
try:
temperature = await tz.get_temperature.asyn()
except TargetStableCalledProcessError as e:
if error == 'raise':
raise e
elif error == 'ignore':
self.logger.warning(f'Skipping thermal_zone_id={tzid} thermal_zone_type={tz.type} error="{e}"')
return None
else:
raise ValueError(f'Unknown error parameter value: {error}')
return temperature

tz_temps = await self.target.async_manager.map_concurrently(get_temperature_noexcep, self.zones.items())

return {tz.type: temperature for (tzid, tz), temperature in tz_temps.items() if temperature is not None}
41 changes: 34 additions & 7 deletions external/devlib/devlib/target.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,14 @@


FSTAB_ENTRY_REGEX = re.compile(r'(\S+) on (.+) type (\S+) \((\S+)\)')
ANDROID_SCREEN_STATE_REGEX = re.compile('(?:mPowerState|mScreenOn|mWakefulness|Display Power: state)=([0-9]+|true|false|ON|OFF|DOZE|Asleep|Awake)',
ANDROID_SCREEN_STATE_REGEX = re.compile('(?:mPowerState|mScreenOn|mWakefulness|Display Power: state)=([0-9]+|true|false|ON|OFF|DOZE|Dozing|Asleep|Awake)',
re.IGNORECASE)
ANDROID_SCREEN_RESOLUTION_REGEX = re.compile(r'cur=(?P<width>\d+)x(?P<height>\d+)')
ANDROID_SCREEN_ROTATION_REGEX = re.compile(r'orientation=(?P<rotation>[0-3])')
DEFAULT_SHELL_PROMPT = re.compile(r'^.*(shell|root|juno)@?.*:[/~]\S* *[#$] ',
re.MULTILINE)
KVERSION_REGEX = re.compile(
r'(?P<version>\d+)(\.(?P<major>\d+)(\.(?P<minor>\d+)(-rc(?P<rc>\d+))?)?)?(-(?P<commits>\d+)-g(?P<sha1>[0-9a-fA-F]{7,}))?'
r'(?P<version>\d+)(\.(?P<major>\d+)(\.(?P<minor>\d+))?(-rc(?P<rc>\d+))?)?(-android(?P<android_version>[0-9]+))?(-(?P<commits>\d+)-g(?P<sha1>[0-9a-fA-F]{7,}))?(-ab(?P<gki_abi>[0-9]+))?'
)

GOOGLE_DNS_SERVER_ADDRESS = '8.8.8.8'
Expand Down Expand Up @@ -1178,6 +1178,17 @@ async def remove(self, path, as_root=False):
await self.execute.asyn('rm -rf -- {}'.format(quote(path)), as_root=as_root)

# misc
@asyn.asyncf
async def read_sysctl(self, parameter):
"""
Returns the value of the given sysctl parameter as a string.
"""
path = target.path.join('proc', 'sys', *parameter.split('.'))
try:
return await self.read_value.asyn(path)
except FileNotFoundError as e:
raise ValueError(f'systcl parameter {parameter} was not found: {e}')

def core_cpus(self, core):
return [i for i, c in enumerate(self.core_names) if c == core]

Expand Down Expand Up @@ -2018,10 +2029,7 @@ async def capture_ui_hierarchy(self, filepath):

parsed_xml = xml.dom.minidom.parse(filepath)
with open(filepath, 'w') as f:
if sys.version_info[0] == 3:
f.write(parsed_xml.toprettyxml())
else:
f.write(parsed_xml.toprettyxml().encode('utf-8'))
f.write(parsed_xml.toprettyxml())

@asyn.asyncf
async def is_installed(self, name):
Expand Down Expand Up @@ -2099,6 +2107,8 @@ async def grant_package_permission(self, package, permission):
pass # Ignore if not requested
elif 'Operation not allowed' in e.message:
pass # Ignore if not allowed
elif 'is managed by role' in e.message:
pass # Ignore if cannot be granted
else:
raise

Expand Down Expand Up @@ -2202,13 +2212,20 @@ def wait_for_device(self, timeout=30):
def reboot_bootloader(self, timeout=30):
self.conn.reboot_bootloader()

@asyn.asyncf
async def is_screen_locked(self):
screen_state = await self.execute.asyn('dumpsys window')
return 'mDreamingLockscreen=true' in screen_state

@asyn.asyncf
async def is_screen_on(self):
output = await self.execute.asyn('dumpsys power')
match = ANDROID_SCREEN_STATE_REGEX.search(output)
if match:
if 'DOZE' in match.group(1).upper():
return True
if match.group(1) == 'Dozing':
return False
if match.group(1) == 'Asleep':
return False
if match.group(1) == 'Awake':
Expand All @@ -2222,7 +2239,7 @@ async def ensure_screen_is_on(self, verify=True):
if not await self.is_screen_on.asyn():
self.execute('input keyevent 26')
if verify and not await self.is_screen_on.asyn():
raise TargetStableError('Display cannot be turned on.')
raise TargetStableError('Display cannot be turned on.')

@asyn.asyncf
async def ensure_screen_is_on_and_stays(self, verify=True, mode=7):
Expand Down Expand Up @@ -2527,6 +2544,10 @@ class KernelVersion(object):
:type commits: int
:ivar sha1: Kernel git revision hash, if available (otherwise None)
:type sha1: str
:ivar android_version: Android version, if available (otherwise None)
:type android_version: int
:ivar gki_abi: GKI kernel abi, if available (otherwise None)
:type gki_abi: str
:ivar parts: Tuple of version number components. Can be used for
lexicographically comparing kernel versions.
Expand All @@ -2550,6 +2571,8 @@ def __init__(self, version_string):
self.sha1 = None
self.rc = None
self.commits = None
self.gki_abi = None
self.android_version = None
match = KVERSION_REGEX.match(version_string)
if match:
groups = match.groupdict()
Expand All @@ -2563,6 +2586,10 @@ def __init__(self, version_string):
self.commits = int(groups['commits'])
if groups['sha1'] is not None:
self.sha1 = match.group('sha1')
if groups['gki_abi'] is not None:
self.gki_abi = match.group('gki_abi')
if groups['android_version'] is not None:
self.android_version = int(match.group('android_version'))

self.parts = (self.version_number, self.major, self.minor)

Expand Down
Loading

0 comments on commit a94c533

Please sign in to comment.