#pylint: disable=C0111
# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import glob
import json
import logging
import os
import platform
import re
import signal
import tempfile
import time
import uuid
from autotest_lib.client.common_lib import error
from autotest_lib.client.common_lib import utils
from autotest_lib.client.bin import base_utils
_AMD_PCI_IDS_FILE_PATH = '/usr/local/autotest/bin/amd_pci_ids.json'
_INTEL_PCI_IDS_FILE_PATH = '/usr/local/autotest/bin/intel_pci_ids.json'
_UI_USE_FLAGS_FILE_PATH = '/etc/ui_use_flags.txt'
# Command to check if a package is installed. If the package is not installed
# the command shall fail.
_CHECK_PACKAGE_INSTALLED_COMMAND =(
"dpkg-query -W -f='${Status}\n' %s | head -n1 | awk '{print $3;}' | "
"grep -q '^installed$'")
pciid_to_amd_architecture = {}
pciid_to_intel_architecture = {}
class Crossystem(object):
"""A wrapper for the crossystem utility."""
def __init__(self, client):
self.cros_system_data = {}
self._client = client
def init(self):
self.cros_system_data = {}
(_, fname) = tempfile.mkstemp()
f = open(fname, 'w')
self._client.run('crossystem', stdout_tee=f)
f.close()
text = utils.read_file(fname)
for line in text.splitlines():
assignment_string = line.split('#')[0]
if not assignment_string.count('='):
continue
(name, value) = assignment_string.split('=', 1)
self.cros_system_data[name.strip()] = value.strip()
os.remove(fname)
def __getattr__(self, name):
"""
Retrieve a crosssystem attribute.
The call crossystemobject.name() will return the crossystem reported
string.
"""
return lambda: self.cros_system_data[name]
def get_oldest_pid_by_name(name):
"""
Return the oldest pid of a process whose name perfectly matches |name|.
name is an egrep expression, which will be matched against the entire name
of processes on the system. For example:
get_oldest_pid_by_name('chrome')
on a system running
8600 ? 00:00:04 chrome
8601 ? 00:00:00 chrome
8602 ? 00:00:00 chrome-sandbox
would return 8600, as that's the oldest process that matches.
chrome-sandbox would not be matched.
Arguments:
name: egrep expression to match. Will be anchored at the beginning and
end of the match string.
Returns:
pid as an integer, or None if one cannot be found.
Raises:
ValueError if pgrep returns something odd.
"""
str_pid = utils.system_output('pgrep -o ^%s$' % name,
ignore_status=True).rstrip()
if str_pid:
return int(str_pid)
def get_oldest_by_name(name):
"""Return pid and command line of oldest process whose name matches |name|.
@param name: egrep expression to match desired process name.
@return: A tuple of (pid, command_line) of the oldest process whose name
matches |name|.
"""
pid = get_oldest_pid_by_name(name)
if pid:
command_line = utils.system_output('ps -p %i -o command=' % pid,
ignore_status=True).rstrip()
return (pid, command_line)
def get_chrome_remote_debugging_port():
"""Returns remote debugging port for Chrome.
Parse chrome process's command line argument to get the remote debugging
port.
"""
_, command = get_oldest_by_name('chrome')
matches = re.search('--remote-debugging-port=([0-9]+)', command)
if matches:
return int(matches.group(1))
def get_process_list(name, command_line=None):
"""
Return the list of pid for matching process |name command_line|.
on a system running
31475 ? 0:06 /opt/google/chrome/chrome --allow-webui-compositing -
31478 ? 0:00 /opt/google/chrome/chrome-sandbox /opt/google/chrome/
31485 ? 0:00 /opt/google/chrome/chrome --type=zygote --log-level=1
31532 ? 1:05 /opt/google/chrome/chrome --type=renderer
get_process_list('chrome')
would return ['31475', '31485', '31532']
get_process_list('chrome', '--type=renderer')
would return ['31532']
Arguments:
name: process name to search for. If command_line is provided, name is
matched against full command line. If command_line is not provided,
name is only matched against the process name.
command line: when command line is passed, the full process command line
is used for matching.
Returns:
list of PIDs of the matching processes.
"""
# TODO(rohitbm) crbug.com/268861
flag = '-x' if not command_line else '-f'
name = '\'%s.*%s\'' % (name, command_line) if command_line else name
str_pid = utils.system_output('pgrep %s %s' % (flag, name),
ignore_status=True).rstrip()
return str_pid.split()
def nuke_process_by_name(name, with_prejudice=False):
"""Tell the oldest process specified by name to exit.
Arguments:
name: process name specifier, as understood by pgrep.
with_prejudice: if True, don't allow for graceful exit.
Raises:
error.AutoservPidAlreadyDeadError: no existing process matches name.
"""
try:
pid = get_oldest_pid_by_name(name)
except Exception as e:
logging.error(e)
return
if pid is None:
raise error.AutoservPidAlreadyDeadError('No process matching %s.' %
name)
if with_prejudice:
utils.nuke_pid(pid, [signal.SIGKILL])
else:
utils.nuke_pid(pid)
def ensure_processes_are_dead_by_name(name, timeout_sec=10):
"""Terminate all processes specified by name and ensure they're gone.
Arguments:
name: process name specifier, as understood by pgrep.
timeout_sec: maximum number of seconds to wait for processes to die.
Raises:
error.AutoservPidAlreadyDeadError: no existing process matches name.
site_utils.TimeoutError: if processes still exist after timeout_sec.
"""
def list_and_kill_processes(name):
process_list = get_process_list(name)
try:
for pid in [int(str_pid) for str_pid in process_list]:
utils.nuke_pid(pid)
except error.AutoservPidAlreadyDeadError:
pass
return process_list
utils.poll_for_condition(lambda: list_and_kill_processes(name) == [],
timeout=timeout_sec)
def is_virtual_machine():
return 'QEMU' in platform.processor()
def save_vm_state(checkpoint):
"""Saves the current state of the virtual machine.
This function is a NOOP if the test is not running under a virtual machine
with the USB serial port redirected.
Arguments:
checkpoint - Name used to identify this state
Returns:
None
"""
# The QEMU monitor has been redirected to the guest serial port located at
# /dev/ttyUSB0. To save the state of the VM, we just send the 'savevm'
# command to the serial port.
if is_virtual_machine() and os.path.exists('/dev/ttyUSB0'):
logging.info('Saving VM state "%s"', checkpoint)
serial = open('/dev/ttyUSB0', 'w')
serial.write('savevm %s\r\n' % checkpoint)
logging.info('Done saving VM state "%s"', checkpoint)
def check_raw_dmesg(dmesg, message_level, whitelist):
"""Checks dmesg for unexpected warnings.
This function parses dmesg for message with message_level <= message_level
which do not appear in the whitelist.
Arguments:
dmesg - string containing raw dmesg buffer
message_level - minimum message priority to check
whitelist - messages to ignore
Returns:
List of unexpected warnings
"""
whitelist_re = re.compile(r'(%s)' % '|'.join(whitelist))
unexpected = []
for line in dmesg.splitlines():
if int(line[1]) <= message_level:
stripped_line = line.split('] ', 1)[1]
if whitelist_re.search(stripped_line):
continue
unexpected.append(stripped_line)
return unexpected
def verify_mesg_set(mesg, regex, whitelist):
"""Verifies that the exact set of messages are present in a text.
This function finds all strings in the text matching a certain regex, and
then verifies that all expected strings are present in the set, and no
unexpected strings are there.
Arguments:
mesg - the mutiline text to be scanned
regex - regular expression to match
whitelist - messages to find in the output, a list of strings
(potentially regexes) to look for in the filtered output. All these
strings must be there, and no other strings should be present in the
filtered output.
Returns:
string of inconsistent findings (i.e. an empty string on success).
"""
rv = []
missing_strings = []
present_strings = []
for line in mesg.splitlines():
if not re.search(r'%s' % regex, line):
continue
present_strings.append(line.split('] ', 1)[1])
for string in whitelist:
for present_string in list(present_strings):
if re.search(r'^%s$' % string, present_string):
present_strings.remove(present_string)
break
else:
missing_strings.append(string)
if present_strings:
rv.append('unexpected strings:')
rv.extend(present_strings)
if missing_strings:
rv.append('missing strings:')
rv.extend(missing_strings)
return '\n'.join(rv)
def target_is_pie():
"""Returns whether the toolchain produces a PIE (position independent
executable) by default.
Arguments:
None
Returns:
True if the target toolchain produces a PIE by default.
False otherwise.
"""
command = 'echo | ${CC} -E -dD -P - | grep -i pie'
result = utils.system_output(command,
retain_output=True,
ignore_status=True)
if re.search('#define __PIE__', result):
return True
else:
return False
def target_is_x86():
"""Returns whether the toolchain produces an x86 object
Arguments:
None
Returns:
True if the target toolchain produces an x86 object
False otherwise.
"""
command = 'echo | ${CC} -E -dD -P - | grep -i 86'
result = utils.system_output(command,
retain_output=True,
ignore_status=True)
if re.search('__i386__', result) or re.search('__x86_64__', result):
return True
else:
return False
def mounts():
ret = []
for line in file('/proc/mounts'):
m = re.match(
r'(?P<src>\S+) (?P<dest>\S+) (?P<type>\S+) (?P<opts>\S+).*', line)
if m:
ret.append(m.groupdict())
return ret
def is_mountpoint(path):
return path in [m['dest'] for m in mounts()]
def require_mountpoint(path):
"""
Raises an exception if path is not a mountpoint.
"""
if not is_mountpoint(path):
raise error.TestFail('Path not mounted: "%s"' % path)
def random_username():
return str(uuid.uuid4()) + '@example.com'
def get_signin_credentials(filepath):
"""Returns user_id, password tuple from credentials file at filepath.
File must have one line of the format user_id:password
@param filepath: path of credentials file.
@return user_id, password tuple.
"""
user_id, password = None, None
if os.path.isfile(filepath):
with open(filepath) as f:
user_id, password = f.read().rstrip().split(':')
return user_id, password
def parse_cmd_output(command, run_method=utils.run):
"""Runs a command on a host object to retrieve host attributes.
The command should output to stdout in the format of:
<key> = <value> # <optional_comment>
@param command: Command to execute on the host.
@param run_method: Function to use to execute the command. Defaults to
utils.run so that the command will be executed locally.
Can be replace with a host.run call so that it will
execute on a DUT or external machine. Method must accept
a command argument, stdout_tee and stderr_tee args and
return a result object with a string attribute stdout
which will be parsed.
@returns a dictionary mapping host attributes to their values.
"""
result = {}
# Suppresses stdout so that the files are not printed to the logs.
cmd_result = run_method(command, stdout_tee=None, stderr_tee=None)
for line in cmd_result.stdout.splitlines():
# Lines are of the format "<key> = <value> # <comment>"
key_value = re.match(r'^\s*(?P<key>[^ ]+)\s*=\s*(?P<value>[^ '
r']+)(?:\s*#.*)?$', line)
if key_value:
result[key_value.group('key')] = key_value.group('value')
return result
def set_from_keyval_output(out, delimiter=' '):
"""Parse delimiter-separated key-val output into a set of tuples.
Output is expected to be multiline text output from a command.
Stuffs the key-vals into tuples in a set to be later compared.
e.g. deactivated 0
disableForceClear 0
==> set(('deactivated', '0'), ('disableForceClear', '0'))
@param out: multiple lines of space-separated key-val pairs.
@param delimiter: character that separates key from val. Usually a
space but may be '=' or something else.
@return set of key-val tuples.
"""
results = set()
kv_match_re = re.compile('([^ ]+)%s(.*)' % delimiter)
for linecr in out.splitlines():
match = kv_match_re.match(linecr.strip())
if match:
results.add((match.group(1), match.group(2)))
return results
def get_cpu_usage():
"""Returns machine's CPU usage.
This function uses /proc/stat to identify CPU usage.
Returns:
A dictionary with 'user', 'nice', 'system' and 'idle' values.
Sample dictionary:
{
'user': 254544,
'nice': 9,
'system': 254768,
'idle': 2859878,
}
"""
proc_stat = open('/proc/stat')
cpu_usage_str = proc_stat.readline().split()
proc_stat.close()
return {
'user': int(cpu_usage_str[1]),
'nice': int(cpu_usage_str[2]),
'system': int(cpu_usage_str[3]),
'idle': int(cpu_usage_str[4])
}
def compute_active_cpu_time(cpu_usage_start, cpu_usage_end):
"""Computes the fraction of CPU time spent non-idling.
This function should be invoked using before/after values from calls to
get_cpu_usage().
"""
time_active_end = (
cpu_usage_end['user'] + cpu_usage_end['nice'] + cpu_usage_end['system'])
time_active_start = (cpu_usage_start['user'] + cpu_usage_start['nice'] +
cpu_usage_start['system'])
total_time_end = (cpu_usage_end['user'] + cpu_usage_end['nice'] +
cpu_usage_end['system'] + cpu_usage_end['idle'])
total_time_start = (cpu_usage_start['user'] + cpu_usage_start['nice'] +
cpu_usage_start['system'] + cpu_usage_start['idle'])
return ((float(time_active_end) - time_active_start) /
(total_time_end - total_time_start))
def is_pgo_mode():
return 'USE_PGO' in os.environ
def wait_for_idle_cpu(timeout, utilization):
"""Waits for the CPU to become idle (< utilization).
Args:
timeout: The longest time in seconds to wait before throwing an error.
utilization: The CPU usage below which the system should be considered
idle (between 0 and 1.0 independent of cores/hyperthreads).
"""
time_passed = 0.0
fraction_active_time = 1.0
sleep_time = 1
logging.info('Starting to wait up to %.1fs for idle CPU...', timeout)
while fraction_active_time >= utilization:
cpu_usage_start = get_cpu_usage()
# Split timeout interval into not too many chunks to limit log spew.
# Start at 1 second, increase exponentially
time.sleep(sleep_time)
time_passed += sleep_time
sleep_time = min(16.0, 2.0 * sleep_time)
cpu_usage_end = get_cpu_usage()
fraction_active_time = \
compute_active_cpu_time(cpu_usage_start, cpu_usage_end)
logging.info('After waiting %.1fs CPU utilization is %.3f.',
time_passed, fraction_active_time)
if time_passed > timeout:
logging.warning('CPU did not become idle.')
log_process_activity()
# crosbug.com/37389
if is_pgo_mode():
logging.info('Still continuing because we are in PGO mode.')
return True
return False
logging.info('Wait for idle CPU took %.1fs (utilization = %.3f).',
time_passed, fraction_active_time)
return True
def log_process_activity():
"""Logs the output of top.
Useful to debug performance tests and to find runaway processes.
"""
logging.info('Logging current process activity using top and ps.')
cmd = 'top -b -n1 -c'
output = utils.run(cmd)
logging.info(output)
output = utils.run('ps axl')
logging.info(output)
def wait_for_cool_machine():
"""
A simple heuristic to wait for a machine to cool.
The code looks a bit 'magic', but we don't know ambient temperature
nor machine characteristics and still would like to return the caller
a machine that cooled down as much as reasonably possible.
"""
temperature = get_current_temperature_max()
# We got here with a cold machine, return immediately. This should be the
# most common case.
if temperature < 50:
return True
logging.info('Got a hot machine of %dC. Sleeping 1 minute.', temperature)
# A modest wait should cool the machine.
time.sleep(60.0)
temperature = get_current_temperature_max()
# Atoms idle below 60 and everyone else should be even lower.
if temperature < 62:
return True
# This should be rare.
logging.info('Did not cool down (%dC). Sleeping 2 minutes.', temperature)
time.sleep(120.0)
temperature = get_current_temperature_max()
# A temperature over 65'C doesn't give us much headroom to the critical
# temperatures that start at 85'C (and PerfControl as of today will fail at
# critical - 10'C).
if temperature < 65:
return True
logging.warning('Did not cool down (%dC), giving up.', temperature)
log_process_activity()
return False
# System paths for machine performance state.
_CPUINFO = '/proc/cpuinfo'
_DIRTY_WRITEBACK_CENTISECS = '/proc/sys/vm/dirty_writeback_centisecs'
_KERNEL_MAX = '/sys/devices/system/cpu/kernel_max'
_MEMINFO = '/proc/meminfo'
_TEMP_SENSOR_RE = 'Reading temperature...([0-9]*)'
def _get_line_from_file(path, line):
"""
line can be an integer or
line can be a string that matches the beginning of the line
"""
with open(path) as f:
if isinstance(line, int):
l = f.readline()
for _ in range(0, line):
l = f.readline()
return l
else:
for l in f:
if l.startswith(line):
return l
return None
def _get_match_from_file(path, line, prefix, postfix):
"""
Matches line in path and returns string between first prefix and postfix.
"""
match = _get_line_from_file(path, line)
# Strip everything from front of line including prefix.
if prefix:
match = re.split(prefix, match)[1]
# Strip everything from back of string including first occurence of postfix.
if postfix:
match = re.split(postfix, match)[0]
return match
def _get_float_from_file(path, line, prefix, postfix):
match = _get_match_from_file(path, line, prefix, postfix)
return float(match)
def _get_int_from_file(path, line, prefix, postfix):
match = _get_match_from_file(path, line, prefix, postfix)
return int(match)
def _get_hex_from_file(path, line, prefix, postfix):
match = _get_match_from_file(path, line, prefix, postfix)
return int(match, 16)
# The paths don't change. Avoid running find all the time.
_hwmon_paths = None
def _get_hwmon_paths(file_pattern):
"""
Returns a list of paths to the temperature sensors.
"""
# Some systems like daisy_spring only have the virtual hwmon.
# And other systems like rambi only have coretemp.0. See crbug.com/360249.
# /sys/class/hwmon/hwmon*/
# /sys/devices/virtual/hwmon/hwmon*/
# /sys/devices/platform/coretemp.0/
if not _hwmon_paths:
cmd = 'find /sys/ -name "' + file_pattern + '"'
_hwon_paths = utils.run(cmd, verbose=False).stdout.splitlines()
return _hwon_paths
def get_temperature_critical():
"""
Returns temperature at which we will see some throttling in the system.
"""
min_temperature = 1000.0
paths = _get_hwmon_paths('temp*_crit')
for path in paths:
temperature = _get_float_from_file(path, 0, None, None) * 0.001
# Today typical for Intel is 98'C to 105'C while ARM is 85'C. Clamp to
# the lowest known value.
if (min_temperature < 60.0) or min_temperature > 150.0:
logging.warning('Critical temperature of %.1fC was reset to 85.0C.',
min_temperature)
min_temperature = 85.0
min_temperature = min(temperature, min_temperature)
return min_temperature
def get_temperature_input_max():
"""
Returns the maximum currently observed temperature.
"""
max_temperature = -1000.0
paths = _get_hwmon_paths('temp*_input')
for path in paths:
temperature = _get_float_from_file(path, 0, None, None) * 0.001
max_temperature = max(temperature, max_temperature)
return max_temperature
def get_thermal_zone_temperatures():
"""
Returns the maximum currently observered temperature in thermal_zones.
"""
temperatures = []
for path in glob.glob('/sys/class/thermal/thermal_zone*/temp'):
try:
temperatures.append(
_get_float_from_file(path, 0, None, None) * 0.001)
except IOError:
# Some devices (e.g. Veyron) may have reserved thermal zones that
# are not active. Trying to read the temperature value would cause a
# EINVAL IO error.
continue
return temperatures
def get_ec_temperatures():
"""
Uses ectool to return a list of all sensor temperatures in Celsius.
"""
temperatures = []
try:
full_cmd = 'ectool temps all'
lines = utils.run(full_cmd, verbose=False).stdout.splitlines()
for line in lines:
temperature = int(line.split(': ')[1]) - 273
temperatures.append(temperature)
except Exception:
logging.warning('Unable to read temperature sensors using ectool.')
for temperature in temperatures:
# Sanity check for real world values.
assert ((temperature > 10.0) and
(temperature < 150.0)), ('Unreasonable temperature %.1fC.' %
temperature)
return temperatures
def get_current_temperature_max():
"""
Returns the highest reported board temperature (all sensors) in Celsius.
"""
temperature = max([get_temperature_input_max()] +
get_thermal_zone_temperatures() +
get_ec_temperatures())
# Sanity check for real world values.
assert ((temperature > 10.0) and
(temperature < 150.0)), ('Unreasonable temperature %.1fC.' %
temperature)
return temperature
def get_cpu_cache_size():
"""
Returns the last level CPU cache size in kBytes.
"""
cache_size = _get_int_from_file(_CPUINFO, 'cache size', ': ', ' KB')
# Sanity check.
assert cache_size >= 64, 'Unreasonably small cache.'
return cache_size
def get_cpu_model_frequency():
"""
Returns the model frequency from the CPU model name on Intel only. This
might be redundant with get_cpu_max_frequency. Unit is Hz.
"""
frequency = _get_float_from_file(_CPUINFO, 'model name', ' @ ', 'GHz')
return 1.e9 * frequency
def get_cpu_max_frequency():
"""
Returns the largest of the max CPU core frequencies. The unit is Hz.
"""
max_frequency = -1
paths = _get_cpufreq_paths('cpuinfo_max_freq')
for path in paths:
# Convert from kHz to Hz.
frequency = 1000 * _get_float_from_file(path, 0, None, None)
max_frequency = max(frequency, max_frequency)
# Sanity check.
assert max_frequency > 1e8, 'Unreasonably low CPU frequency.'
return max_frequency
def get_cpu_min_frequency():
"""
Returns the smallest of the minimum CPU core frequencies.
"""
min_frequency = 1e20
paths = _get_cpufreq_paths('cpuinfo_min_freq')
for path in paths:
frequency = _get_float_from_file(path, 0, None, None)
min_frequency = min(frequency, min_frequency)
# Sanity check.
assert min_frequency > 1e8, 'Unreasonably low CPU frequency.'
return min_frequency
def get_cpu_model():
"""
Returns the CPU model.
Only works on Intel.
"""
cpu_model = _get_int_from_file(_CPUINFO, 'model\t', ': ', None)
return cpu_model
def get_cpu_family():
"""
Returns the CPU family.
Only works on Intel.
"""
cpu_family = _get_int_from_file(_CPUINFO, 'cpu family\t', ': ', None)
return cpu_family
def get_board_property(key):
"""
Get a specific property from /etc/lsb-release.
@param key: board property to return value for
@return the value or '' if not present
"""
with open('/etc/lsb-release') as f:
pattern = '%s=(.*)' % key
pat = re.search(pattern, f.read())
if pat:
return pat.group(1)
return ''
def get_board():
"""
Get the ChromeOS release board name from /etc/lsb-release.
"""
return get_board_property('BOARD')
def get_board_type():
"""
Get the ChromeOS board type from /etc/lsb-release.
@return device type.
"""
return get_board_property('DEVICETYPE')
def get_board_with_frequency_and_memory():
"""
Returns a board name modified with CPU frequency and memory size to
differentiate between different board variants. For instance
link -> link_1.8GHz_4GB.
"""
board_name = get_board()
if is_virtual_machine():
board = '%s_VM' % board_name
else:
# Rounded to nearest GB and GHz.
memory = int(round(get_mem_total() / 1024.0))
# Convert frequency to GHz with 1 digit accuracy after the decimal point.
frequency = int(round(get_cpu_max_frequency() * 1e-8)) * 0.1
board = '%s_%1.1fGHz_%dGB' % (board_name, frequency, memory)
return board
def get_mem_total():
"""
Returns the total memory available in the system in MBytes.
"""
mem_total = _get_float_from_file(_MEMINFO, 'MemTotal:', 'MemTotal:', ' kB')
# Sanity check, all Chromebooks have at least 1GB of memory.
assert mem_total > 256 * 1024, 'Unreasonable amount of memory.'
return mem_total / 1024
def get_mem_free():
"""
Returns the currently free memory in the system in MBytes.
"""
mem_free = _get_float_from_file(_MEMINFO, 'MemFree:', 'MemFree:', ' kB')
return mem_free / 1024
def get_kernel_max():
"""
Returns content of kernel_max.
"""
kernel_max = _get_int_from_file(_KERNEL_MAX, 0, None, None)
# Sanity check.
assert ((kernel_max > 0) and (kernel_max < 257)), 'Unreasonable kernel_max.'
return kernel_max
def set_high_performance_mode():
"""
Sets the kernel governor mode to the highest setting.
Returns previous governor state.
"""
original_governors = get_scaling_governor_states()
set_scaling_governors('performance')
return original_governors
def set_scaling_governors(value):
"""
Sets all scaling governor to string value.
Sample values: 'performance', 'interactive', 'ondemand', 'powersave'.
"""
paths = _get_cpufreq_paths('scaling_governor')
for path in paths:
cmd = 'echo %s > %s' % (value, path)
logging.info('Writing scaling governor mode \'%s\' -> %s', value, path)
# On Tegra CPUs can be dynamically enabled/disabled. Ignore failures.
utils.system(cmd, ignore_status=True)
def _get_cpufreq_paths(filename):
"""
Returns a list of paths to the governors.
"""
cmd = 'ls /sys/devices/system/cpu/cpu*/cpufreq/' + filename
paths = utils.run(cmd, verbose=False).stdout.splitlines()
return paths
def get_scaling_governor_states():
"""
Returns a list of (performance governor path, current state) tuples.
"""
paths = _get_cpufreq_paths('scaling_governor')
path_value_list = []
for path in paths:
value = _get_line_from_file(path, 0)
path_value_list.append((path, value))
return path_value_list
def restore_scaling_governor_states(path_value_list):
"""
Restores governor states. Inverse operation to get_scaling_governor_states.
"""
for (path, value) in path_value_list:
cmd = 'echo %s > %s' % (value.rstrip('\n'), path)
# On Tegra CPUs can be dynamically enabled/disabled. Ignore failures.
utils.system(cmd, ignore_status=True)
def get_dirty_writeback_centisecs():
"""
Reads /proc/sys/vm/dirty_writeback_centisecs.
"""
time = _get_int_from_file(_DIRTY_WRITEBACK_CENTISECS, 0, None, None)
return time
def set_dirty_writeback_centisecs(time=60000):
"""
In hundredths of a second, this is how often pdflush wakes up to write data
to disk. The default wakes up the two (or more) active threads every five
seconds. The ChromeOS default is 10 minutes.
We use this to set as low as 1 second to flush error messages in system
logs earlier to disk.
"""
# Flush buffers first to make this function synchronous.
utils.system('sync')
if time >= 0:
cmd = 'echo %d > %s' % (time, _DIRTY_WRITEBACK_CENTISECS)
utils.system(cmd)
def wflinfo_cmd():
"""
Returns a wflinfo command appropriate to the current graphics platform/api.
"""
return 'wflinfo -p %s -a %s' % (graphics_platform(), graphics_api())
def has_mali():
""" @return: True if system has a Mali GPU enabled."""
return os.path.exists('/dev/mali0')
def get_gpu_family():
"""Returns the GPU family name."""
global pciid_to_amd_architecture
global pciid_to_intel_architecture
socfamily = base_utils.get_cpu_soc_family()
if socfamily == 'exynos5' or socfamily == 'rockchip' or has_mali():
cmd = wflinfo_cmd()
wflinfo = utils.system_output(cmd,
retain_output=True,
ignore_status=False)
version = re.findall(r'OpenGL renderer string: '
r'Mali-T([0-9]+)', wflinfo)
if version:
return 'mali-t%s' % version[0]
return 'mali-unrecognized'
if socfamily == 'tegra':
return 'tegra'
if os.path.exists('/sys/kernel/debug/pvr'):
return 'rogue'
pci_vga_device = utils.run("lspci | grep VGA").stdout.rstrip('\n')
bus_device_function = pci_vga_device.partition(' ')[0]
pci_path = '/sys/bus/pci/devices/0000:' + bus_device_function + '/device'
if not os.path.exists(pci_path):
raise error.TestError('PCI device 0000:' + bus_device_function + ' not found')
device_id = utils.read_one_line(pci_path).lower()
if "Advanced Micro Devices" in pci_vga_device:
if not pciid_to_amd_architecture:
with open(_AMD_PCI_IDS_FILE_PATH, 'r') as in_f:
pciid_to_amd_architecture = json.load(in_f)
return pciid_to_amd_architecture[device_id]
if "Intel Corporation" in pci_vga_device:
# Only load Intel PCI ID file once and only if necessary.
if not pciid_to_intel_architecture:
with open(_INTEL_PCI_IDS_FILE_PATH, 'r') as in_f:
pciid_to_intel_architecture = json.load(in_f)
return pciid_to_intel_architecture[device_id]
# TODO(ihf): Consider using /etc/lsb-release DEVICETYPE != CHROMEBOOK/CHROMEBASE
# for sanity check, but usage seems a bit inconsistent. See
# src/third_party/chromiumos-overlay/eclass/appid.eclass
_BOARDS_WITHOUT_MONITOR = [
'anglar', 'mccloud', 'monroe', 'ninja', 'rikku', 'guado', 'jecht', 'tidus',
'veyron_brian', 'beltino', 'panther', 'stumpy', 'panther', 'tricky', 'zako',
'veyron_rialto'
]
def has_no_monitor():
"""Returns whether a machine doesn't have a built-in monitor."""
board_name = get_board()
if board_name in _BOARDS_WITHOUT_MONITOR:
return True
return False
def get_fixed_dst_drive():
"""
Return device name for internal disk.
Example: return /dev/sda for falco booted from usb
"""
cmd = ' '.join(['. /usr/sbin/write_gpt.sh;',
'. /usr/share/misc/chromeos-common.sh;',
'load_base_vars;',
'get_fixed_dst_drive'])
return utils.system_output(cmd)
def get_root_device():
"""
Return root device.
Will return correct disk device even system boot from /dev/dm-0
Example: return /dev/sdb for falco booted from usb
"""
return utils.system_output('rootdev -s -d')
def get_root_partition():
"""
Return current root partition
Example: return /dev/sdb3 for falco booted from usb
"""
return utils.system_output('rootdev -s')
def get_free_root_partition(root_part=None):
"""
Return currently unused root partion
Example: return /dev/sdb5 for falco booted from usb
@param root_part: cuurent root partition
"""
spare_root_map = {'3': '5', '5': '3'}
if not root_part:
root_part = get_root_partition()
return root_part[:-1] + spare_root_map[root_part[-1]]
def get_kernel_partition(root_part=None):
"""
Return current kernel partition
Example: return /dev/sda2 for falco booted from usb
@param root_part: current root partition
"""
if not root_part:
root_part = get_root_partition()
current_kernel_map = {'3': '2', '5': '4'}
return root_part[:-1] + current_kernel_map[root_part[-1]]
def get_free_kernel_partition(root_part=None):
"""
return currently unused kernel partition
Example: return /dev/sda4 for falco booted from usb
@param root_part: current root partition
"""
kernel_part = get_kernel_partition(root_part)
spare_kernel_map = {'2': '4', '4': '2'}
return kernel_part[:-1] + spare_kernel_map[kernel_part[-1]]
def is_booted_from_internal_disk():
"""Return True if boot from internal disk. False, otherwise."""
return get_root_device() == get_fixed_dst_drive()
def get_ui_use_flags():
"""Parses the USE flags as listed in /etc/ui_use_flags.txt.
@return: A list of flag strings found in the ui use flags file.
"""
flags = []
for flag in utils.read_file(_UI_USE_FLAGS_FILE_PATH).splitlines():
# Removes everything after the '#'.
flag_before_comment = flag.split('#')[0].strip()
if len(flag_before_comment) != 0:
flags.append(flag_before_comment)
return flags
def is_freon():
"""Returns False if the system uses X, True otherwise."""
return 'X' not in get_ui_use_flags()
def graphics_platform():
"""
Return a string identifying the graphics platform,
e.g. 'glx' or 'x11_egl' or 'gbm'
"""
use_flags = get_ui_use_flags()
if 'X' not in use_flags:
return 'null'
elif 'opengles' in use_flags:
return 'x11_egl'
return 'glx'
def graphics_api():
"""Return a string identifying the graphics api, e.g. gl or gles2."""
use_flags = get_ui_use_flags()
if 'opengles' in use_flags:
return 'gles2'
return 'gl'
def assert_has_X_server():
"""Using X is soon to be deprecated. Print warning or raise error."""
if is_freon():
# TODO(ihf): Think about if we could support X for testing for a while.
raise error.TestFail('freon: can\'t use X server.')
logging.warning('freon: Using the X server will be deprecated soon.')
def is_vm():
"""Check if the process is running in a virtual machine.
@return: True if the process is running in a virtual machine, otherwise
return False.
"""
try:
virt = utils.run('sudo -n virt-what').stdout.strip()
logging.debug('virt-what output: %s', virt)
return bool(virt)
except error.CmdError:
logging.warn('Package virt-what is not installed, default to assume '
'it is not a virtual machine.')
return False
def is_package_installed(package):
"""Check if a package is installed already.
@return: True if the package is already installed, otherwise return False.
"""
try:
utils.run(_CHECK_PACKAGE_INSTALLED_COMMAND % package)
return True
except error.CmdError:
logging.warn('Package %s is not installed.', package)
return False
def is_python_package_installed(package):
"""Check if a Python package is installed already.
@return: True if the package is already installed, otherwise return False.
"""
try:
__import__(package)
return True
except ImportError:
logging.warn('Python package %s is not installed.', package)
return False
def run_sql_cmd(server, user, password, command, database=''):
"""Run the given sql command against the specified database.
@param server: Hostname or IP address of the MySQL server.
@param user: User name to log in the MySQL server.
@param password: Password to log in the MySQL server.
@param command: SQL command to run.
@param database: Name of the database to run the command. Default to empty
for command that does not require specifying database.
@return: The stdout of the command line.
"""
cmd = ('mysql -u%s -p%s --host %s %s -e "%s"' %
(user, password, server, database, command))
# Set verbose to False so the command line won't be logged, as it includes
# database credential.
return utils.run(cmd, verbose=False).stdout